summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:39:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:39:28 +0000
commit924f5ea83e48277e014ebf0d19a27187cb93e2f7 (patch)
tree75920a275bba045f6d108204562c218a9a26ea15
parentAdding upstream version 2.1.7. (diff)
downloadpacemaker-upstream.tar.xz
pacemaker-upstream.zip
Adding upstream version 2.1.8~rc1.upstream/2.1.8_rc1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--COPYING3
-rw-r--r--ChangeLog464
-rw-r--r--GNUmakefile4
-rw-r--r--INSTALL.md17
-rw-r--r--Makefile.am2
-rw-r--r--README.markdown5
-rw-r--r--SECURITY.md18
-rwxr-xr-xagents/ocf/HealthCPU.in4
-rwxr-xr-xagents/ocf/HealthIOWait.in26
-rwxr-xr-xagents/ocf/HealthSMART.in46
-rwxr-xr-xagents/ocf/SysInfo.in6
-rwxr-xr-xagents/ocf/o2cb.in3
-rw-r--r--configure.ac61
-rw-r--r--cts/README.md9
-rw-r--r--cts/cli/regression.access_render.exp46
-rw-r--r--cts/cli/regression.acls.exp246
-rw-r--r--cts/cli/regression.daemons.exp736
-rw-r--r--cts/cli/regression.rules.exp8
-rw-r--r--cts/cli/regression.tools.exp2600
-rw-r--r--cts/cli/regression.upgrade.exp53
-rw-r--r--cts/cli/regression.validity.exp298
-rw-r--r--cts/cli/tickets.xml32
-rw-r--r--cts/cts-attrd.in2
-rwxr-xr-xcts/cts-cli.in300
-rw-r--r--cts/cts-fencing.in28
-rw-r--r--cts/cts-log-watcher.in3
-rw-r--r--cts/scheduler/exp/utilization-order4.exp2
-rw-r--r--cts/scheduler/stderr/order-wrong-kind.stderr2
-rw-r--r--cts/scheduler/summary/order-wrong-kind.summary2
-rw-r--r--cts/valgrind-pcmk.suppressions8
-rw-r--r--daemons/attrd/attrd_alerts.c14
-rw-r--r--daemons/attrd/attrd_attributes.c125
-rw-r--r--daemons/attrd/attrd_cib.c316
-rw-r--r--daemons/attrd/attrd_corosync.c295
-rw-r--r--daemons/attrd/attrd_elections.c10
-rw-r--r--daemons/attrd/attrd_ipc.c99
-rw-r--r--daemons/attrd/attrd_messages.c57
-rw-r--r--daemons/attrd/attrd_sync.c48
-rw-r--r--daemons/attrd/attrd_utils.c43
-rw-r--r--daemons/attrd/pacemaker-attrd.c10
-rw-r--r--daemons/attrd/pacemaker-attrd.h104
-rw-r--r--daemons/based/Makefile.am9
-rw-r--r--daemons/based/based_callbacks.c361
-rw-r--r--daemons/based/based_io.c35
-rw-r--r--daemons/based/based_messages.c286
-rw-r--r--daemons/based/based_notify.c64
-rw-r--r--daemons/based/based_operation.c1
-rw-r--r--daemons/based/based_remote.c63
-rw-r--r--daemons/based/based_transaction.c34
-rw-r--r--daemons/based/pacemaker-based.c61
-rw-r--r--daemons/based/pacemaker-based.h6
-rw-r--r--daemons/controld/controld_attrd.c28
-rw-r--r--daemons/controld/controld_callbacks.c32
-rw-r--r--daemons/controld/controld_cib.c184
-rw-r--r--daemons/controld/controld_cib.h6
-rw-r--r--daemons/controld/controld_control.c287
-rw-r--r--daemons/controld/controld_corosync.c40
-rw-r--r--daemons/controld/controld_election.c43
-rw-r--r--daemons/controld/controld_execd.c217
-rw-r--r--daemons/controld/controld_execd_state.c63
-rw-r--r--daemons/controld/controld_fencing.c111
-rw-r--r--daemons/controld/controld_fsa.c11
-rw-r--r--daemons/controld/controld_fsa.h6
-rw-r--r--daemons/controld/controld_join_client.c84
-rw-r--r--daemons/controld/controld_join_dc.c113
-rw-r--r--daemons/controld/controld_membership.c101
-rw-r--r--daemons/controld/controld_messages.c270
-rw-r--r--daemons/controld/controld_metadata.c51
-rw-r--r--daemons/controld/controld_remote_ra.c130
-rw-r--r--daemons/controld/controld_schedulerd.c76
-rw-r--r--daemons/controld/controld_te_actions.c142
-rw-r--r--daemons/controld/controld_te_callbacks.c367
-rw-r--r--daemons/controld/controld_te_events.c106
-rw-r--r--daemons/controld/controld_te_utils.c71
-rw-r--r--daemons/controld/controld_throttle.c42
-rw-r--r--daemons/controld/controld_timers.c73
-rw-r--r--daemons/controld/controld_transition.c41
-rw-r--r--daemons/controld/controld_utils.c14
-rw-r--r--daemons/controld/pacemaker-controld.c28
-rw-r--r--daemons/controld/pacemaker-controld.h3
-rw-r--r--daemons/execd/Makefile.am4
-rw-r--r--daemons/execd/cts-exec-helper.c14
-rw-r--r--daemons/execd/execd_alerts.c36
-rw-r--r--daemons/execd/execd_commands.c224
-rw-r--r--daemons/execd/pacemaker-execd.c53
-rw-r--r--daemons/execd/pacemaker-execd.h1
-rw-r--r--daemons/execd/remoted_pidone.c10
-rw-r--r--daemons/execd/remoted_proxy.c71
-rw-r--r--daemons/execd/remoted_schemas.c286
-rw-r--r--daemons/execd/remoted_tls.c24
-rw-r--r--daemons/fenced/cts-fence-helper.c15
-rw-r--r--daemons/fenced/fenced_cib.c219
-rw-r--r--daemons/fenced/fenced_commands.c579
-rw-r--r--daemons/fenced/fenced_history.c242
-rw-r--r--daemons/fenced/fenced_remote.c459
-rw-r--r--daemons/fenced/fenced_scheduler.c15
-rw-r--r--daemons/fenced/pacemaker-fenced.c392
-rw-r--r--daemons/fenced/pacemaker-fenced.h14
-rw-r--r--daemons/pacemakerd/Makefile.am3
-rw-r--r--daemons/pacemakerd/pacemakerd.c34
-rw-r--r--daemons/pacemakerd/pacemakerd.h12
-rw-r--r--daemons/pacemakerd/pcmkd_corosync.c20
-rw-r--r--daemons/pacemakerd/pcmkd_corosync.h16
-rw-r--r--daemons/pacemakerd/pcmkd_messages.c55
-rw-r--r--daemons/pacemakerd/pcmkd_subdaemons.c116
-rw-r--r--daemons/schedulerd/pacemaker-schedulerd.c32
-rw-r--r--daemons/schedulerd/pacemaker-schedulerd.h3
-rw-r--r--daemons/schedulerd/schedulerd_messages.c61
-rw-r--r--devel/Makefile.am4
-rw-r--r--doc/README.md32
-rw-r--r--doc/sphinx/Makefile.am25
-rw-r--r--doc/sphinx/Pacemaker_Administration/agents.rst1247
-rw-r--r--doc/sphinx/Pacemaker_Administration/configuring.rst55
-rw-r--r--doc/sphinx/Pacemaker_Administration/index.rst1
-rw-r--r--doc/sphinx/Pacemaker_Administration/installing.rst6
-rw-r--r--doc/sphinx/Pacemaker_Administration/options.rst178
-rw-r--r--doc/sphinx/Pacemaker_Administration/pcs-crmsh.rst3
-rw-r--r--doc/sphinx/Pacemaker_Administration/upgrading.rst30
-rw-r--r--doc/sphinx/Pacemaker_Development/c.rst125
-rw-r--r--doc/sphinx/Pacemaker_Development/components.rst48
-rw-r--r--doc/sphinx/Pacemaker_Development/documentation.rst35
-rw-r--r--doc/sphinx/Pacemaker_Development/faq.rst27
-rw-r--r--doc/sphinx/Pacemaker_Development/general.rst10
-rw-r--r--doc/sphinx/Pacemaker_Development/glossary.rst84
-rw-r--r--doc/sphinx/Pacemaker_Development/index.rst2
-rw-r--r--doc/sphinx/Pacemaker_Explained/alerts.rst28
-rw-r--r--doc/sphinx/Pacemaker_Explained/cluster-options.rst242
-rw-r--r--doc/sphinx/Pacemaker_Explained/collective.rst411
-rw-r--r--doc/sphinx/Pacemaker_Explained/constraints.rst241
-rw-r--r--doc/sphinx/Pacemaker_Explained/fencing.rst623
-rw-r--r--doc/sphinx/Pacemaker_Explained/local-options.rst301
-rw-r--r--doc/sphinx/Pacemaker_Explained/nodes.rst38
-rw-r--r--doc/sphinx/Pacemaker_Explained/operations.rst257
-rw-r--r--doc/sphinx/Pacemaker_Explained/resources.rst470
-rw-r--r--doc/sphinx/Pacemaker_Explained/reusing-configuration.rst12
-rw-r--r--doc/sphinx/Pacemaker_Explained/rules.rst1293
-rw-r--r--doc/sphinx/Pacemaker_Explained/utilization.rst252
-rw-r--r--doc/sphinx/Pacemaker_Remote/options.rst4
-rw-r--r--doc/sphinx/conf.py.in6
-rw-r--r--etc/sysconfig/pacemaker.in12
-rw-r--r--include/Makefile.am6
-rw-r--r--include/crm/Makefile.am4
-rw-r--r--include/crm/cib.h16
-rw-r--r--include/crm/cib/cib_types.h58
-rw-r--r--include/crm/cib/internal.h43
-rw-r--r--include/crm/cib/util.h12
-rw-r--r--include/crm/cib/util_compat.h22
-rw-r--r--include/crm/cib_compat.h34
-rw-r--r--include/crm/cluster.h203
-rw-r--r--include/crm/cluster/compat.h159
-rw-r--r--include/crm/cluster/internal.h104
-rw-r--r--include/crm/common/Makefile.am12
-rw-r--r--include/crm/common/acl.h8
-rw-r--r--include/crm/common/acl_internal.h7
-rw-r--r--include/crm/common/action_relation_internal.h51
-rw-r--r--include/crm/common/actions.h237
-rw-r--r--include/crm/common/actions_internal.h71
-rw-r--r--include/crm/common/agents.h4
-rw-r--r--include/crm/common/agents_compat.h4
-rw-r--r--include/crm/common/alerts_internal.h6
-rw-r--r--include/crm/common/attrs_internal.h (renamed from include/crm/common/attrd_internal.h)15
-rw-r--r--include/crm/common/bundles_internal.h89
-rw-r--r--include/crm/common/cib.h4
-rw-r--r--include/crm/common/cib_internal.h4
-rw-r--r--include/crm/common/clone_internal.h52
-rw-r--r--include/crm/common/digests_internal.h15
-rw-r--r--include/crm/common/failcounts_internal.h4
-rw-r--r--include/crm/common/group_internal.h24
-rw-r--r--include/crm/common/history_internal.h52
-rw-r--r--include/crm/common/internal.h72
-rw-r--r--include/crm/common/io_internal.h4
-rw-r--r--include/crm/common/ipc.h28
-rw-r--r--include/crm/common/ipc_attrd_internal.h11
-rw-r--r--include/crm/common/ipc_controld.h4
-rw-r--r--include/crm/common/ipc_internal.h18
-rw-r--r--include/crm/common/ipc_pacemakerd.h18
-rw-r--r--include/crm/common/ipc_schedulerd.h4
-rw-r--r--include/crm/common/iso8601.h30
-rw-r--r--include/crm/common/iso8601_internal.h4
-rw-r--r--include/crm/common/location_internal.h36
-rw-r--r--include/crm/common/logging.h125
-rw-r--r--include/crm/common/logging_compat.h6
-rw-r--r--include/crm/common/logging_internal.h96
-rw-r--r--include/crm/common/mainloop.h30
-rw-r--r--include/crm/common/mainloop_compat.h8
-rw-r--r--include/crm/common/messages_internal.h3
-rw-r--r--include/crm/common/nodes.h198
-rw-r--r--include/crm/common/nodes_internal.h27
-rw-r--r--include/crm/common/nvpair.h23
-rw-r--r--include/crm/common/nvpair_internal.h63
-rw-r--r--include/crm/common/options.h231
-rw-r--r--include/crm/common/options_internal.h204
-rw-r--r--include/crm/common/output.h4
-rw-r--r--include/crm/common/output_internal.h107
-rw-r--r--include/crm/common/primitive_internal.h39
-rw-r--r--include/crm/common/remote_internal.h65
-rw-r--r--include/crm/common/resources.h268
-rw-r--r--include/crm/common/resources_internal.h25
-rw-r--r--include/crm/common/results.h76
-rw-r--r--include/crm/common/results_compat.h4
-rw-r--r--include/crm/common/results_internal.h23
-rw-r--r--include/crm/common/roles.h14
-rw-r--r--include/crm/common/roles_internal.h48
-rw-r--r--include/crm/common/rules.h112
-rw-r--r--include/crm/common/rules_internal.h38
-rw-r--r--include/crm/common/scheduler.h207
-rw-r--r--include/crm/common/scheduler_internal.h118
-rw-r--r--include/crm/common/scheduler_types.h4
-rw-r--r--include/crm/common/schemas.h32
-rw-r--r--include/crm/common/schemas_internal.h37
-rw-r--r--include/crm/common/scores.h33
-rw-r--r--include/crm/common/scores_compat.h37
-rw-r--r--include/crm/common/scores_internal.h15
-rw-r--r--include/crm/common/strings_internal.h22
-rw-r--r--include/crm/common/tags.h14
-rw-r--r--include/crm/common/tickets.h20
-rw-r--r--include/crm/common/unittest_internal.h85
-rw-r--r--include/crm/common/util.h56
-rw-r--r--include/crm/common/util_compat.h29
-rw-r--r--include/crm/common/xml.h224
-rw-r--r--include/crm/common/xml_compat.h127
-rw-r--r--include/crm/common/xml_internal.h234
-rw-r--r--include/crm/common/xml_io.h48
-rw-r--r--include/crm/common/xml_io_compat.h58
-rw-r--r--include/crm/common/xml_io_internal.h34
-rw-r--r--include/crm/common/xml_names.h458
-rw-r--r--include/crm/common/xml_names_internal.h353
-rw-r--r--include/crm/compatibility.h35
-rw-r--r--include/crm/crm.h25
-rw-r--r--include/crm/crm_compat.h34
-rw-r--r--include/crm/fencing/internal.h66
-rw-r--r--include/crm/lrmd.h84
-rw-r--r--include/crm/lrmd_compat.h177
-rw-r--r--include/crm/msg_xml.h469
-rw-r--r--include/crm/msg_xml_compat.h924
-rw-r--r--include/crm/pengine/Makefile.am5
-rw-r--r--include/crm/pengine/common.h38
-rw-r--r--include/crm/pengine/common_compat.h57
-rw-r--r--include/crm/pengine/complex.h2
-rw-r--r--include/crm/pengine/internal.h294
-rw-r--r--include/crm/pengine/pe_types_compat.h116
-rw-r--r--include/crm/pengine/remote_internal.h7
-rw-r--r--include/crm/pengine/rules.h42
-rw-r--r--include/crm/pengine/rules_compat.h50
-rw-r--r--include/crm/pengine/rules_internal.h16
-rw-r--r--include/crm/pengine/status.h73
-rw-r--r--include/crm/pengine/status_compat.h72
-rw-r--r--include/crm/services.h7
-rw-r--r--include/crm/stonith-ng.h49
-rw-r--r--include/crm_config.h.in5
-rw-r--r--include/crm_internal.h48
-rw-r--r--include/pacemaker-internal.h5
-rw-r--r--include/pacemaker.h174
-rw-r--r--include/pcmki/pcmki_options.h19
-rw-r--r--include/pcmki/pcmki_resource.h4
-rw-r--r--include/pcmki/pcmki_ticket.h154
-rw-r--r--include/pcmki/pcmki_transition.h3
-rw-r--r--include/pcmki/pcmki_verify.h49
-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
-rw-r--r--m4/version.m42
-rw-r--r--mk/tap.mk17
-rw-r--r--mk/unittest.mk15
-rw-r--r--po/zh_CN.po1510
-rw-r--r--python/Makefile.am12
-rw-r--r--python/pacemaker/__init__.py6
-rw-r--r--python/pacemaker/_cts/CTS.py137
-rw-r--r--python/pacemaker/_cts/__init__.py6
-rw-r--r--python/pacemaker/_cts/audits.py350
-rw-r--r--python/pacemaker/_cts/cib.py65
-rw-r--r--python/pacemaker/_cts/cibxml.py547
-rw-r--r--python/pacemaker/_cts/clustermanager.py212
-rw-r--r--python/pacemaker/_cts/cmcorosync.py17
-rw-r--r--python/pacemaker/_cts/corosync.py52
-rw-r--r--python/pacemaker/_cts/environment.py128
-rw-r--r--python/pacemaker/_cts/errors.py22
-rw-r--r--python/pacemaker/_cts/input.py8
-rw-r--r--python/pacemaker/_cts/logging.py45
-rw-r--r--python/pacemaker/_cts/network.py34
-rw-r--r--python/pacemaker/_cts/patterns.py188
-rw-r--r--python/pacemaker/_cts/process.py18
-rw-r--r--python/pacemaker/_cts/remote.py143
-rw-r--r--python/pacemaker/_cts/scenarios.py154
-rw-r--r--python/pacemaker/_cts/test.py295
-rw-r--r--python/pacemaker/_cts/tests/__init__.py14
-rw-r--r--python/pacemaker/_cts/tests/componentfail.py23
-rw-r--r--python/pacemaker/_cts/tests/ctstest.py112
-rw-r--r--python/pacemaker/_cts/tests/fliptest.py18
-rw-r--r--python/pacemaker/_cts/tests/maintenancemode.py38
-rw-r--r--python/pacemaker/_cts/tests/nearquorumpointtest.py30
-rw-r--r--python/pacemaker/_cts/tests/partialstart.py23
-rw-r--r--python/pacemaker/_cts/tests/reattach.py51
-rw-r--r--python/pacemaker/_cts/tests/remotebasic.py18
-rw-r--r--python/pacemaker/_cts/tests/remotedriver.py152
-rw-r--r--python/pacemaker/_cts/tests/remotemigrate.py21
-rw-r--r--python/pacemaker/_cts/tests/remoterscfailure.py26
-rw-r--r--python/pacemaker/_cts/tests/remotestonithd.py26
-rw-r--r--python/pacemaker/_cts/tests/resourcerecover.py35
-rw-r--r--python/pacemaker/_cts/tests/restartonebyone.py18
-rw-r--r--python/pacemaker/_cts/tests/restarttest.py18
-rw-r--r--python/pacemaker/_cts/tests/resynccib.py23
-rw-r--r--python/pacemaker/_cts/tests/simulstart.py18
-rw-r--r--python/pacemaker/_cts/tests/simulstartlite.py31
-rw-r--r--python/pacemaker/_cts/tests/simulstop.py18
-rw-r--r--python/pacemaker/_cts/tests/simulstoplite.py33
-rw-r--r--python/pacemaker/_cts/tests/splitbraintest.py36
-rw-r--r--python/pacemaker/_cts/tests/standbytest.py22
-rw-r--r--python/pacemaker/_cts/tests/startonebyone.py18
-rw-r--r--python/pacemaker/_cts/tests/starttest.py24
-rw-r--r--python/pacemaker/_cts/tests/stonithdtest.py24
-rw-r--r--python/pacemaker/_cts/tests/stoponebyone.py18
-rw-r--r--python/pacemaker/_cts/tests/stoptest.py26
-rw-r--r--python/pacemaker/_cts/timer.py44
-rw-r--r--python/pacemaker/_cts/watcher.py489
-rw-r--r--python/pacemaker/buildoptions.py.in49
-rw-r--r--python/pacemaker/exitstatus.py99
-rw-r--r--python/tests/test_cts_network.py1
-rw-r--r--python/tests/test_exitstatus.py1
-rw-r--r--rpm/Makefile.am11
-rw-r--r--rpm/pacemaker.spec.in11
-rw-r--r--rpm/rpmlintrc9
-rw-r--r--tools/attrd_updater.c12
-rw-r--r--tools/cibadmin.c209
-rw-r--r--tools/cibsecret.in2
-rw-r--r--tools/crm_attribute.c316
-rw-r--r--tools/crm_diff.c50
-rw-r--r--tools/crm_error.c4
-rw-r--r--tools/crm_mon.c324
-rw-r--r--tools/crm_mon.h3
-rw-r--r--tools/crm_mon_curses.c28
-rw-r--r--tools/crm_node.c87
-rw-r--r--tools/crm_resource.c1101
-rw-r--r--tools/crm_resource.h26
-rw-r--r--tools/crm_resource_ban.c188
-rw-r--r--tools/crm_resource_print.c271
-rw-r--r--tools/crm_resource_runtime.c779
-rw-r--r--tools/crm_rule.c15
-rw-r--r--tools/crm_shadow.c94
-rw-r--r--tools/crm_simulate.c52
-rw-r--r--tools/crm_ticket.c614
-rw-r--r--tools/crm_verify.c174
-rw-r--r--tools/crmadmin.c14
-rw-r--r--tools/stonith_admin.c23
-rw-r--r--xml/Makefile.am63
-rw-r--r--xml/README.md2
-rw-r--r--xml/alerts-3.10.rng82
-rw-r--r--xml/api/crm_attribute-2.34.rng33
-rw-r--r--xml/api/crm_attribute-2.36.rng33
-rw-r--r--xml/api/crm_mon-2.35.rng197
-rw-r--r--xml/api/crm_resource-2.36.rng289
-rw-r--r--xml/api/crm_resource-2.37.rng348
-rw-r--r--xml/api/crm_ticket-2.35.rng34
-rw-r--r--xml/api/ocf-ra-1.1.rng221
-rw-r--r--xml/api/options-2.34.rng37
-rw-r--r--xml/api/options-2.36.rng46
-rw-r--r--xml/api/stonith_admin-2.33.rng55
-rw-r--r--xml/api/ticket-2.35.rng68
-rwxr-xr-xxml/best-match.sh98
-rw-r--r--xml/constraints-3.10.rng287
-rw-r--r--xml/constraints-next.rng4
-rw-r--r--xml/nodes-3.10.rng46
-rw-r--r--xml/nvset-3.10.rng68
-rw-r--r--xml/options-3.10.rng133
-rw-r--r--xml/resources-3.10.rng451
-rwxr-xr-xxml/rng-helper.in251
-rw-r--r--xml/rule-3.10.rng433
-rw-r--r--xml/version-diff.sh.in60
630 files changed, 56526 insertions, 28979 deletions
diff --git a/COPYING b/COPYING
index 1bf7219..7ffee16 100644
--- a/COPYING
+++ b/COPYING
@@ -10,5 +10,4 @@ The text of these licenses are provided in the "licenses" subdirectory.
If you find any deviations from this policy, or wish to inquire about alternate
licensing arrangements, please e-mail the developers@ClusterLabs.org mailing
-list. Licensing issues are further discussed on the ClusterLabs wiki
-(at https://wiki.clusterlabs.org/wiki/License).
+list.
diff --git a/ChangeLog b/ChangeLog
index e5ecf98..8211238 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,465 @@
+* Wed May 15 2024 Ken Gaillot <kgaillot@redhat.com> Pacemaker-2.1.8-rc1
+- 2480 commits with 507 files changed, 45891 insertions(+), 22991 deletions(-)
+
+- Features added since Pacemaker-2.1.7
+ + CIB: deprecate omitting validate-with from the CIB or setting it to "none"
+ or an unknown schema
+ + CIB: deprecate "default" and "#default" as explicit meta-attribute values
+ + CIB: deprecate resource-discovery-enabled node attribute
+ + CIB: deprecate support for multiple top-level rules within a location
+ constraint (a single rule may still contain multiple sub-rules)
+ + CIB: deprecate support for node attribute expressions in rules beneath op,
+ op_defaults, or fence device meta-attributes
+ + CIB: deprecate support for rkt in bundles
+ + CIB: drop support for (nonworking) rules based on the #role node attribute
+ (role-based location constraints may still contain rules)
+ + CIB manager,controller,fencer,scheduler: deprecate "metadata" command-line
+ option (instead, use crm_attribute --list-options mentioned below)
+ + pacemaker-remoted: newer schema files are now downloaded from the cluster,
+ allowing more command-line tools to work when the Pacemaker Remote node
+ has an older Pacemaker version
+ + agents: deprecate the ocf:pacemaker:o2cb resource agent
+ + tools: crm_attribute --list-options lists all possible cluster options
+ + tools: crm_resource --list-options lists all possible primitive
+ meta-attributes or special fence device parameters
+ + tools: new --score option for cibadmin --modify and crm_attribute --update
+ enables expansion of "++" and "+=" in attribute values without a warning
+ (using such expansions without --score is now deprecated)
+ + tools: crm_ticket supports standard --output-as/--output-to arguments
+ + tools: deprecate --text-fancy standard command-line option
+
+- Fixes since Pacemaker-2.1.7
+ + tools: restore the (deprecated) ability to automatically correct malformed
+ XML passed via standard input (regression introduced in 2.1.7)
+ + CIB: restore the (deprecated) ability to use validate-with="pacemaker-next"
+ (regression introduced in 2.1.6)
+ + controller: avoid zombie children when asynchronous actions exit while a
+ synchronous meta-data action is in progress
+ (regression introduced in 2.1.5)
+ + libcrmcommon: avoid file descriptor leak in asynchronous IPC clients
+ (regression introduced in 2.1.3)
+ + tools: crm_mon no longer crashes on some platforms when the fencer
+ connection is lost (regression introduced in 2.1.0)
+ + attribute manager: write Pacemaker Remote node attributes even if node is
+ not cached
+ + attribute manager: avoid use-after-free when remote node in cluster node
+ cache
+ + attribute manager: correctly propagate utilization attributes to peers
+ to avoid the possibility of later being written out as regular node
+ attributes
+ + fencer: correctly parse action-specific timeouts with units other than
+ seconds
+ + fencer: avoid unnecessary timeouts when the watchdog timeout is greater
+ than a query timeout, per-device fencing timeout, or stonith-timeout
+ + libcrmcommon: don't assume next schema will validate when not transforming
+ + libcrmcommon: when displaying XML, don't show "<null>" for empty attribute
+ values, and properly escape special characters
+ + scheduler: if the user specifies a timeout of 0, use the default 20s as
+ documented
+ + agents: ocf:pacemaker:SysInfo respects attrd_updater dampening
+ + agents: ocf:pacemaker:HealthSMART properly handles SMART data missing
+ temperature
+ + tools: cibadmin --replace now leaves "++" and "+=" unexpanded in XML
+ attribute values rather than wrongly treat them as 0
+ + tools: cibsecret avoids possible truncation issue in process listing
+ + tools: crm_attribute --node localhost or --node auto works
+ + tools: crm_resource ignores resource meta-attribute node expressions
+ for consistency with how the cluster works
+ + tools: crm_resource honors rules when getting utilization attributes
+ + tools: crm_verify --output-as=xml includes detailed messages
+ + tools: crm_mon exits upon loss of an attached pseudo-terminal to avoid
+ possibility of 100% CPU usage (seen when run via sudo with use_pty
+ configured)
+
+- Public API changes since Pacemaker-2.1.7
+ + libcib: add cib_score_update cib_call_options value
+ + libcib: deprecate functions cib_get_generation(), cib_metadata(),
+ cib_pref(), query_node_uname(), and set_standby()
+ + libcib: deprecate T_CIB_DIFF_NOTIFY
+ + libcib: deprecate `<failed>` element in CIB create reply
+ + libcrmcluster: add enum pcmk_cluster_layer
+ + libcrmcluster: add functions pcmk_cluster_connect(),
+ pcmk_cluster_disconnect(), pcmk_cluster_layer_text(),
+ pcmk_cluster_set_destroy_fn(), pcmk_cpg_set_confchg_fn(),
+ pcmk_cpg_set_deliver_fn(), and pcmk_get_cluster_layer()
+ + libcrmcluster: add type pcmk_cluster_t
+ + libcrmcluster: deprecate functions cluster_connect_cpg(),
+ cluster_disconnect_cpg(), crm_active_peers(), crm_cluster_connect(),
+ crm_cluster_disconnect(), crm_get_peer(), crm_get_peer_full(),
+ crm_is_corosync_peer_active(), crm_is_peer_active(), crm_join_phase_str(),
+ crm_peer_destroy(), crm_peer_init(), crm_peer_uname(),
+ crm_peer_uuid(), crm_remote_node_cache_size(),
+ crm_remote_peer_cache_refresh(), crm_remote_peer_cache_remove(),
+ crm_remote_peer_get(), crm_set_autoreap(), crm_set_status_callback(),
+ get_cluster_type(), get_local_nodeid(), get_local_node_name(),
+ get_node_name(), is_corosync_cluster(), name_for_cluster_type(),
+ pcmk_cpg_membership(), pcmk_message_common_cs(), reap_crm_member(),
+ send_cluster_message(), send_cluster_text(), and text2msg_type()
+ + libcrmcluster: deprecate enums crm_ais_msg_types, crm_status_type,
+ cluster_type_e, crm_ais_msg_class, crm_get_peer_flags, crm_join_phase,
+ and crm_node_flags, including all their values
+ + libcrmcluster: deprecate global variables crm_have_quorum, crm_peer_cache,
+ crm_peer_seq, and crm_remote_peer_cache
+ + libcrmcluster: deprecate crm_cluster_t and struct crm_cluster_s, including
+ all its members
+ + libcrmcluster: deprecate crm_node_t and struct crm_peer_node_s, including
+ all its members
+ + libcrmcluster: deprecate constants CRM_NODE_LOST and CRM_NODE_MEMBER
+ + libcrmcommon: add constants PCMK_ACTION_METADATA, PCMK_META_ALLOW_MIGRATE,
+ PCMK_META_ALLOW_UNHEALTHY_NODES, PCMK_META_CONTAINER_ATTRIBUTE_TARGET,
+ PCMK_META_CRITICAL, PCMK_META_GLOBALLY_UNIQUE, PCMK_META_INTERLEAVE,
+ PCMK_META_INTERVAL, PCMK_META_INTERVAL_ORIGIN, PCMK_META_IS_MANAGED,
+ PCMK_META_MAINTENANCE, PCMK_META_MULTIPLE_ACTIVE, PCMK_META_NOTIFY,
+ PCMK_META_ON_FAIL, PCMK_META_ORDERED, PCMK_META_PRIORITY,
+ PCMK_META_PROMOTABLE, PCMK_META_RECORD_PENDING, PCMK_META_REMOTE_ADDR,
+ PCMK_META_REMOTE_ALLOW_MIGRATE, PCMK_META_REMOTE_CONNECT_TIMEOUT,
+ PCMK_META_REMOTE_NODE, PCMK_META_REMOTE_PORT, PCMK_META_REQUIRES,
+ PCMK_META_RESOURCE_STICKINESS, PCMK_META_START_DELAY,
+ PCMK_META_TARGET_ROLE, PCMK_META_TIMEOUT, PCMK_META_TIMESTAMP_FORMAT,
+ PCMK_NODE_ATTR_MAINTENANCE, PCMK_NODE_ATTR_STANDBY, PCMK_OPT_BATCH_LIMIT,
+ PCMK_OPT_CLUSTER_DELAY, PCMK_OPT_CLUSTER_INFRASTRUCTURE,
+ PCMK_OPT_CLUSTER_IPC_LIMIT, PCMK_OPT_CLUSTER_NAME,
+ PCMK_OPT_CLUSTER_RECHECK_INTERVAL, PCMK_OPT_CONCURRENT_FENCING,
+ PCMK_OPT_DC_DEADTIME, PCMK_OPT_DC_VERSION, PCMK_OPT_ELECTION_TIMEOUT,
+ PCMK_OPT_ENABLE_ACL, PCMK_OPT_ENABLE_STARTUP_PROBES,
+ PCMK_OPT_FENCE_REACTION, PCMK_OPT_HAVE_WATCHDOG,
+ PCMK_OPT_JOIN_FINALIZATION_TIMEOUT, PCMK_OPT_JOIN_INTEGRATION_TIMEOUT,
+ PCMK_OPT_LOAD_THRESHOLD, PCMK_OPT_MAINTENANCE_MODE,
+ PCMK_OPT_MIGRATION_LIMIT, PCMK_OPT_NODE_ACTION_LIMIT,
+ PCMK_OPT_NODE_HEALTH_BASE, PCMK_OPT_NODE_HEALTH_GREEN,
+ PCMK_OPT_NODE_HEALTH_RED, PCMK_OPT_NODE_HEALTH_STRATEGY,
+ PCMK_OPT_NODE_HEALTH_YELLOW, PCMK_OPT_NODE_PENDING_TIMEOUT,
+ PCMK_OPT_NO_QUORUM_POLICY, PCMK_OPT_PE_ERROR_SERIES_MAX,
+ PCMK_OPT_PE_INPUT_SERIES_MAX, PCMK_OPT_PE_WARN_SERIES_MAX,
+ PCMK_OPT_PLACEMENT_STRATEGY, PCMK_OPT_PRIORITY_FENCING_DELAY,
+ PCMK_OPT_SHUTDOWN_ESCALATION, PCMK_OPT_SHUTDOWN_LOCK,
+ PCMK_OPT_SHUTDOWN_LOCK_LIMIT, PCMK_OPT_STARTUP_FENCING,
+ PCMK_OPT_START_FAILURE_IS_FATAL, PCMK_OPT_STONITH_ACTION,
+ PCMK_OPT_STONITH_ENABLED, PCMK_OPT_STONITH_MAX_ATTEMPTS,
+ PCMK_OPT_STONITH_TIMEOUT, PCMK_OPT_STONITH_WATCHDOG_TIMEOUT,
+ PCMK_OPT_STOP_ALL_RESOURCES, PCMK_OPT_STOP_ORPHAN_ACTIONS,
+ PCMK_OPT_STOP_ORPHAN_RESOURCES, PCMK_OPT_SYMMETRIC_CLUSTER,
+ PCMK_OPT_TRANSITION_DELAY, PCMK_REMOTE_RA_ADDR, PCMK_REMOTE_RA_PORT,
+ PCMK_REMOTE_RA_RECONNECT_INTERVAL, PCMK_REMOTE_RA_SERVER,
+ PCMK_ROLE_PROMOTED, PCMK_ROLE_STARTED, PCMK_ROLE_STOPPED,
+ PCMK_ROLE_UNPROMOTED, PCMK_SCORE_INFINITY, PCMK_VALUE_ALWAYS,
+ PCMK_VALUE_AND, PCMK_VALUE_BALANCED, PCMK_VALUE_BLOCK, PCMK_VALUE_BOOLEAN,
+ PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, PCMK_VALUE_COROSYNC, PCMK_VALUE_CREATE,
+ PCMK_VALUE_CUSTOM, PCMK_VALUE_DATE_SPEC, PCMK_VALUE_DEFAULT,
+ PCMK_VALUE_DEFINED, PCMK_VALUE_DELETE, PCMK_VALUE_DEMOTE, PCMK_VALUE_DENY,
+ PCMK_VALUE_DURATION, PCMK_VALUE_DYNAMIC_LIST, PCMK_VALUE_EQ,
+ PCMK_VALUE_EXCLUSIVE, PCMK_VALUE_FAILED, PCMK_VALUE_FALSE,
+ PCMK_VALUE_FENCE, PCMK_VALUE_FENCE_LEGACY, PCMK_VALUE_FENCING,
+ PCMK_VALUE_FREEZE, PCMK_VALUE_GRANTED, PCMK_VALUE_GREEN, PCMK_VALUE_GT,
+ PCMK_VALUE_GTE, PCMK_VALUE_HOST, PCMK_VALUE_IGNORE, PCMK_VALUE_IN_RANGE,
+ PCMK_VALUE_INFINITY, PCMK_VALUE_INTEGER, PCMK_VALUE_LITERAL,
+ PCMK_VALUE_LT, PCMK_VALUE_LTE, PCMK_VALUE_MANDATORY, PCMK_VALUE_MEMBER,
+ PCMK_VALUE_META, PCMK_VALUE_MIGRATE_ON_RED, PCMK_VALUE_MINIMAL,
+ PCMK_VALUE_MINUS_INFINITY, PCMK_VALUE_MODIFY, PCMK_VALUE_MOVE,
+ PCMK_VALUE_NE, PCMK_VALUE_NEVER, PCMK_VALUE_NONE,
+ PCMK_VALUE_NONNEGATIVE_INTEGER, PCMK_VALUE_NOTHING,
+ PCMK_VALUE_NOT_DEFINED, PCMK_VALUE_NUMBER, PCMK_VALUE_OFFLINE,
+ PCMK_VALUE_ONLINE, PCMK_VALUE_ONLY_GREEN, PCMK_VALUE_OPTIONAL,
+ PCMK_VALUE_OR, PCMK_VALUE_PANIC, PCMK_VALUE_PARAM, PCMK_VALUE_PENDING,
+ PCMK_VALUE_PERCENTAGE, PCMK_VALUE_PLUS_INFINITY, PCMK_VALUE_PORT,
+ PCMK_VALUE_PROGRESSIVE, PCMK_VALUE_QUORUM, PCMK_VALUE_READ,
+ PCMK_VALUE_RED, PCMK_VALUE_REMOTE, PCMK_VALUE_RESTART,
+ PCMK_VALUE_RESTART_CONTAINER, PCMK_VALUE_REVOKED, PCMK_VALUE_SCORE,
+ PCMK_VALUE_SELECT, PCMK_VALUE_SERIALIZE, PCMK_VALUE_STANDBY,
+ PCMK_VALUE_STATIC_LIST, PCMK_VALUE_STATUS, PCMK_VALUE_STOP,
+ PCMK_VALUE_STOP_ONLY, PCMK_VALUE_STOP_START, PCMK_VALUE_STOP_UNEXPECTED,
+ PCMK_VALUE_STRING, PCMK_VALUE_SUCCESS, PCMK_VALUE_TIMEOUT,
+ PCMK_VALUE_TRUE, PCMK_VALUE_UNFENCING, PCMK_VALUE_UNKNOWN,
+ PCMK_VALUE_UTILIZATION, PCMK_VALUE_VERSION, PCMK_VALUE_WRITE,
+ PCMK_VALUE_YELLOW, PCMK_XA_ACTION, PCMK_XA_ACTIVE, PCMK_XA_ADD_HOST,
+ PCMK_XA_ADMIN_EPOCH, PCMK_XA_ADVANCED, PCMK_XA_AGENT, PCMK_XA_API_VERSION,
+ PCMK_XA_ATTRIBUTE, PCMK_XA_AUTHOR, PCMK_XA_AUTOMATIC, PCMK_XA_BLOCKED,
+ PCMK_XA_BOOLEAN_OP, PCMK_XA_BUILD, PCMK_XA_CACHED, PCMK_XA_CALL,
+ PCMK_XA_CIB_LAST_WRITTEN, PCMK_XA_CIB_NODE, PCMK_XA_CLASS, PCMK_XA_CLIENT,
+ PCMK_XA_CODE, PCMK_XA_COMMENT, PCMK_XA_COMPLETED, PCMK_XA_CONTROL_PORT,
+ PCMK_XA_COUNT, PCMK_XA_CRMD, PCMK_XA_CRM_DEBUG_ORIGIN,
+ PCMK_XA_CRM_FEATURE_SET, PCMK_XA_CRM_TIMESTAMP, PCMK_XA_DAYS,
+ PCMK_XA_DC_UUID, PCMK_XA_DEFAULT, PCMK_XA_DELEGATE, PCMK_XA_DESCRIPTION,
+ PCMK_XA_DEST, PCMK_XA_DEVICE, PCMK_XA_DEVICES, PCMK_XA_DISABLED,
+ PCMK_XA_DURATION, PCMK_XA_END, PCMK_XA_EPOCH, PCMK_XA_EXEC,
+ PCMK_XA_EXECUTION_CODE, PCMK_XA_EXECUTION_DATE, PCMK_XA_EXECUTION_MESSAGE,
+ PCMK_XA_EXEC_TIME, PCMK_XA_EXITCODE, PCMK_XA_EXITREASON,
+ PCMK_XA_EXITSTATUS, PCMK_XA_EXIT_REASON, PCMK_XA_EXPECTED,
+ PCMK_XA_EXPECTED_UP, PCMK_XA_EXTENDED_STATUS, PCMK_XA_FAILED,
+ PCMK_XA_FAILURE_IGNORED, PCMK_XA_FAIL_COUNT, PCMK_XA_FEATURES,
+ PCMK_XA_FEATURE_SET, PCMK_XA_FILE, PCMK_XA_FIRST, PCMK_XA_FIRST_ACTION,
+ PCMK_XA_FOR, PCMK_XA_FUNCTION, PCMK_XA_GENERATED, PCMK_XA_HASH,
+ PCMK_XA_HAVE_QUORUM, PCMK_XA_HEALTH, PCMK_XA_HOST, PCMK_XA_HOST_INTERFACE,
+ PCMK_XA_HOST_NETMASK, PCMK_XA_HOURS, PCMK_XA_ID, PCMK_XA_ID_AS_RESOURCE,
+ PCMK_XA_ID_REF, PCMK_XA_IMAGE, PCMK_XA_INDEX, PCMK_XA_INFLUENCE,
+ PCMK_XA_INSTANCE, PCMK_XA_INTERNAL_PORT, PCMK_XA_INTERVAL,
+ PCMK_XA_IP_RANGE_START, PCMK_XA_IS_DC, PCMK_XA_KIND, PCMK_XA_LANG,
+ PCMK_XA_LAST_FAILURE, PCMK_XA_LAST_GRANTED, PCMK_XA_LAST_RC_CHANGE,
+ PCMK_XA_LAST_UPDATED, PCMK_XA_LOCKED_TO, PCMK_XA_LOCKED_TO_HYPHEN,
+ PCMK_XA_LOSS_POLICY, PCMK_XA_MAINTENANCE, PCMK_XA_MAINTENANCE_MODE,
+ PCMK_XA_MANAGED, PCMK_XA_MESSAGE, PCMK_XA_MINUTES, PCMK_XA_MIXED_VERSION,
+ PCMK_XA_MONTHDAYS, PCMK_XA_MONTHS, PCMK_XA_MULTI_STATE, PCMK_XA_NAME,
+ PCMK_XA_NETWORK, PCMK_XA_NEXT_ROLE, PCMK_XA_NODE, PCMK_XA_NODEID,
+ PCMK_XA_NODES_RUNNING_ON, PCMK_XA_NODE_ATTRIBUTE, PCMK_XA_NODE_NAME,
+ PCMK_XA_NODE_PATH, PCMK_XA_NO_QUORUM_PANIC, PCMK_XA_NO_QUORUM_POLICY,
+ PCMK_XA_NUMBER, PCMK_XA_NUMBER_RESOURCES, PCMK_XA_NUM_UPDATES,
+ PCMK_XA_OBJECT_TYPE, PCMK_XA_ONLINE, PCMK_XA_ON_TARGET, PCMK_XA_OP,
+ PCMK_XA_OPERATION, PCMK_XA_OPTIONS, PCMK_XA_OP_KEY, PCMK_XA_ORIGIN,
+ PCMK_XA_ORPHAN, PCMK_XA_ORPHANED, PCMK_XA_PACEMAKERD_STATE, PCMK_XA_PATH,
+ PCMK_XA_PENDING, PCMK_XA_PORT, PCMK_XA_PRESENT,
+ PCMK_XA_PRIORITY_FENCING_DELAY_MS, PCMK_XA_PROGRAM, PCMK_XA_PROMOTABLE,
+ PCMK_XA_PROMOTED_MAX, PCMK_XA_PROMOTED_ONLY, PCMK_XA_PROVIDER,
+ PCMK_XA_QUEUED, PCMK_XA_QUEUE_TIME, PCMK_XA_QUORUM, PCMK_XA_RANGE,
+ PCMK_XA_RC, PCMK_XA_RC_TEXT, PCMK_XA_REASON, PCMK_XA_REFERENCE,
+ PCMK_XA_RELOADABLE, PCMK_XA_REMAIN_STOPPED, PCMK_XA_REMOTE_CLEAR_PORT,
+ PCMK_XA_REMOTE_NODE, PCMK_XA_REMOTE_TLS_PORT, PCMK_XA_REPLICAS,
+ PCMK_XA_REPLICAS_PER_HOST, PCMK_XA_REQUEST, PCMK_XA_REQUIRE_ALL,
+ PCMK_XA_RESOURCE, PCMK_XA_RESOURCES_RUNNING, PCMK_XA_RESOURCE_AGENT,
+ PCMK_XA_RESOURCE_DISCOVERY, PCMK_XA_RESULT, PCMK_XA_ROLE, PCMK_XA_RSC,
+ PCMK_XA_RSC_PATTERN, PCMK_XA_RSC_ROLE, PCMK_XA_RULE_ID, PCMK_XA_RUNNING,
+ PCMK_XA_RUNNING_ON, PCMK_XA_RUN_COMMAND, PCMK_XA_SCOPE, PCMK_XA_SCORE,
+ PCMK_XA_SCORE_ATTRIBUTE, PCMK_XA_SECONDS, PCMK_XA_SEQUENTIAL,
+ PCMK_XA_SHUTDOWN, PCMK_XA_SOURCE, PCMK_XA_SOURCE_DIR,
+ PCMK_XA_SOURCE_DIR_ROOT, PCMK_XA_SPEC, PCMK_XA_STANDARD, PCMK_XA_STANDBY,
+ PCMK_XA_STANDBY_ONFAIL, PCMK_XA_START, PCMK_XA_STATE, PCMK_XA_STATUS,
+ PCMK_XA_STONITH_ENABLED, PCMK_XA_STONITH_TIMEOUT_MS,
+ PCMK_XA_STOP_ALL_RESOURCES, PCMK_XA_SYMMETRICAL,
+ PCMK_XA_SYMMETRIC_CLUSTER, PCMK_XA_SYS_FROM, PCMK_XA_TAG, PCMK_XA_TARGET,
+ PCMK_XA_TARGET_ATTRIBUTE, PCMK_XA_TARGET_DIR, PCMK_XA_TARGET_PATTERN,
+ PCMK_XA_TARGET_ROLE, PCMK_XA_TARGET_VALUE, PCMK_XA_TASK, PCMK_XA_TEMPLATE,
+ PCMK_XA_THEN, PCMK_XA_THEN_ACTION, PCMK_XA_TICKET, PCMK_XA_TIME,
+ PCMK_XA_TYPE, PCMK_XA_UNAME, PCMK_XA_UNCLEAN, PCMK_XA_UNHEALTHY,
+ PCMK_XA_UNIQUE, PCMK_XA_UNMANAGED, PCMK_XA_UPDATE_CLIENT,
+ PCMK_XA_UPDATE_ORIGIN, PCMK_XA_UPDATE_USER, PCMK_XA_USER, PCMK_XA_VALID,
+ PCMK_XA_VALIDATE_WITH, PCMK_XA_VALUE, PCMK_XA_VALUE_SOURCE,
+ PCMK_XA_VERSION, PCMK_XA_WATCHDOG, PCMK_XA_WEEKDAYS, PCMK_XA_WEEKS,
+ PCMK_XA_WEEKYEARS, PCMK_XA_WEIGHT, PCMK_XA_WHEN, PCMK_XA_WITH_QUORUM,
+ PCMK_XA_WITH_RSC, PCMK_XA_WITH_RSC_ROLE, PCMK_XA_XPATH, PCMK_XA_YEARDAYS,
+ PCMK_XA_YEARS, PCMK_XE_ACLS, PCMK_XE_ACL_GROUP, PCMK_XE_ACL_PERMISSION,
+ PCMK_XE_ACL_ROLE, PCMK_XE_ACL_TARGET, PCMK_XE_ACTION, PCMK_XE_ACTIONS,
+ PCMK_XE_AGENT, PCMK_XE_AGENTS, PCMK_XE_AGENT_STATUS, PCMK_XE_ALERT,
+ PCMK_XE_ALERTS, PCMK_XE_ALLOCATIONS, PCMK_XE_ALLOCATIONS_UTILIZATIONS,
+ PCMK_XE_ATTRIBUTE, PCMK_XE_BAN, PCMK_XE_BANS, PCMK_XE_BUNDLE,
+ PCMK_XE_CAPACITY, PCMK_XE_CHANGE, PCMK_XE_CHANGE_ATTR,
+ PCMK_XE_CHANGE_LIST, PCMK_XE_CHANGE_RESULT, PCMK_XE_CHECK, PCMK_XE_CIB,
+ PCMK_XE_CLONE, PCMK_XE_CLUSTER_ACTION, PCMK_XE_CLUSTER_INFO,
+ PCMK_XE_CLUSTER_OPTIONS, PCMK_XE_CLUSTER_PROPERTY_SET,
+ PCMK_XE_CLUSTER_STATUS, PCMK_XE_COMMAND, PCMK_XE_CONFIGURATION,
+ PCMK_XE_CONSTRAINTS, PCMK_XE_CONTENT, PCMK_XE_CRM_CONFIG, PCMK_XE_CRM_MON,
+ PCMK_XE_CRM_MON_DISCONNECTED, PCMK_XE_CURRENT_DC, PCMK_XE_DATE_SPEC,
+ PCMK_XE_DC, PCMK_XE_DEPRECATED, PCMK_XE_DIFF, PCMK_XE_DIGEST,
+ PCMK_XE_DIGESTS, PCMK_XE_DOCKER, PCMK_XE_DURATION, PCMK_XE_ERROR,
+ PCMK_XE_ERRORS, PCMK_XE_EXPRESSION, PCMK_XE_FAILURE, PCMK_XE_FAILURES,
+ PCMK_XE_FEATURE, PCMK_XE_FEATURES, PCMK_XE_FENCE_EVENT,
+ PCMK_XE_FENCE_HISTORY, PCMK_XE_FENCING_ACTION, PCMK_XE_FENCING_LEVEL,
+ PCMK_XE_FENCING_TOPOLOGY, PCMK_XE_GROUP, PCMK_XE_INJECT_ATTR,
+ PCMK_XE_INJECT_SPEC, PCMK_XE_INSTANCE_ATTRIBUTES, PCMK_XE_INSTRUCTION,
+ PCMK_XE_ITEM, PCMK_XE_LAST_CHANGE, PCMK_XE_LAST_FENCED,
+ PCMK_XE_LAST_UPDATE, PCMK_XE_LIST, PCMK_XE_LONGDESC, PCMK_XE_METADATA,
+ PCMK_XE_META_ATTRIBUTES, PCMK_XE_MODIFICATIONS, PCMK_XE_MODIFY_NODE,
+ PCMK_XE_MODIFY_TICKET, PCMK_XE_NETWORK, PCMK_XE_NODE, PCMK_XE_NODES,
+ PCMK_XE_NODES_CONFIGURED, PCMK_XE_NODE_ACTION, PCMK_XE_NODE_ATTRIBUTES,
+ PCMK_XE_NODE_HISTORY, PCMK_XE_NODE_INFO, PCMK_XE_NODE_WEIGHT,
+ PCMK_XE_NVPAIR, PCMK_XE_OBJ_REF, PCMK_XE_OP, PCMK_XE_OPERATION,
+ PCMK_XE_OPERATIONS, PCMK_XE_OPERATION_HISTORY, PCMK_XE_OPTION,
+ PCMK_XE_OP_DEFAULTS, PCMK_XE_OUTPUT, PCMK_XE_OVERRIDE, PCMK_XE_OVERRIDES,
+ PCMK_XE_PACEMAKERD, PCMK_XE_PACEMAKER_RESULT, PCMK_XE_PARAMETER,
+ PCMK_XE_PARAMETERS, PCMK_XE_PODMAN, PCMK_XE_PORT_MAPPING,
+ PCMK_XE_POSITION, PCMK_XE_PRIMITIVE, PCMK_XE_PROMOTION_SCORE,
+ PCMK_XE_PROVIDER, PCMK_XE_PROVIDERS, PCMK_XE_PSEUDO_ACTION,
+ PCMK_XE_REASON, PCMK_XE_RECIPIENT, PCMK_XE_REPLICA, PCMK_XE_RESOURCE,
+ PCMK_XE_RESOURCES, PCMK_XE_RESOURCES_CONFIGURED, PCMK_XE_RESOURCE_AGENT,
+ PCMK_XE_RESOURCE_AGENT_ACTION, PCMK_XE_RESOURCE_CONFIG,
+ PCMK_XE_RESOURCE_HISTORY, PCMK_XE_RESOURCE_REF, PCMK_XE_RESOURCE_SET,
+ PCMK_XE_RESULT_CODE, PCMK_XE_REVISED_CLUSTER_STATUS,
+ PCMK_XE_ROLE, PCMK_XE_RSC_ACTION, PCMK_XE_RSC_COLOCATION,
+ PCMK_XE_RSC_DEFAULTS, PCMK_XE_RSC_LOCATION, PCMK_XE_RSC_ORDER,
+ PCMK_XE_RSC_TICKET, PCMK_XE_RULE, PCMK_XE_RULE_CHECK, PCMK_XE_SELECT,
+ PCMK_XE_SELECT_ATTRIBUTES, PCMK_XE_SELECT_FENCING, PCMK_XE_SELECT_NODES,
+ PCMK_XE_SELECT_RESOURCES, PCMK_XE_SHADOW, PCMK_XE_SHORTDESC,
+ PCMK_XE_SOURCE, PCMK_XE_SPECIAL, PCMK_XE_STACK, PCMK_XE_STATUS,
+ PCMK_XE_STORAGE, PCMK_XE_STORAGE_MAPPING, PCMK_XE_SUMMARY, PCMK_XE_TAG,
+ PCMK_XE_TAGS, PCMK_XE_TARGET, PCMK_XE_TEMPLATE, PCMK_XE_TICKET,
+ PCMK_XE_TICKETS, PCMK_XE_TIMING, PCMK_XE_TIMINGS, PCMK_XE_TRANSITION,
+ PCMK_XE_UTILIZATION, PCMK_XE_UTILIZATIONS, PCMK_XE_VALIDATE,
+ PCMK_XE_VERSION, PCMK_XE_XML, PCMK_XE_XML_PATCHSET, and
+ XML_CIB_TAG_ALERT_ATTRIBUTES
+ + libcrmcommon: add functions pcmk_action_text(), pcmk_find_node(),
+ pcmk_foreach_active_resource(), pcmk_get_dc(), pcmk_parse_interval_spec(),
+ pcmk_get_no_quorum_policy(), pcmk_has_quorum(),
+ pcmk_node_is_clean(), pcmk_update_configured_schema(),
+ pcmk_node_is_in_maintenance(), pcmk_node_is_online(),
+ pcmk_node_is_pending(), pcmk_node_is_shutting_down(), pcmk_on_fail_text(),
+ pcmk_parse_action(), pcmk_parse_role(), pcmk_resource_id(),
+ pcmk_resource_is_managed(), pcmk_role_text(), and pcmk_set_scheduler_cib()
+ + libcrmcommon: add type pcmk_rule_input_t
+ + libcrmcommon: deprecate globals crm_log_level, crm_trace_nonlog,
+ was_processing_error, and was_processing_warning
+ + libcrmcommon: deprecate functions add_message_xml(), add_node_copy(),
+ can_prune_leaf(), cli_config_update(), copy_in_properties(), copy_xml(),
+ create_hello_message(), pcmk_parse_action(), create_reply(),
+ create_reply_adv(), create_request(), create_request_adv(),
+ create_xml_node(), crm_map_element_name(), crm_next_same_xml(),
+ crm_parse_interval_spec(), crm_xml_escape(), diff_xml_object(),
+ dump_xml_formatted(), dump_xml_formatted_with_text(),
+ dump_xml_unformatted(), expand_plus_plus(), filename2xml(),
+ find_xml_children(), find_xml_node(), first_named_child(),
+ fix_plus_plus_recursive(), get_message_xml(), get_schema_name(),
+ get_schema_version(), get_xpath_object_relative(), ID(),
+ pcmk_action_text(), pcmk_create_html_node(), pcmk_create_xml_text_node(),
+ pcmk_hostname(), pcmk_on_fail_text(), purge_diff_markers(),
+ replace_xml_child(), stdin2xml(), string2xml(), subtract_xml_object(),
+ update_validation(), update_xml_child(), validate_xml(),
+ validate_xml_verbose(), write_xml_fd(), write_xml_file(),
+ xml_latest_schema(), and xml_remove_prop()
+ + libcrmcommon: deprecate constants CIB_OPTIONS_FIRST, CRM_INFINITY_S,
+ CRM_MINUS_INFINITY_S, CRM_OP_LOCAL_SHUTDOWN, CRM_PLUS_INFINITY_S,
+ CRM_SCORE_INFINITY, F_CLIENTNAME, F_CRM_DATA, F_CRM_DC_LEAVING,
+ F_CRM_ELECTION_AGE_S, F_CRM_ELECTION_AGE_US, F_CRM_ELECTION_ID,
+ F_CRM_ELECTION_OWNER, F_CRM_HOST_FROM, F_CRM_HOST_TO, F_CRM_JOIN_ID,
+ F_CRM_MSG_TYPE, F_CRM_ORIGIN, F_CRM_REFERENCE, F_CRM_SYS_FROM,
+ F_CRM_SYS_TO, F_CRM_TASK, F_CRM_TGRAPH, F_CRM_TGRAPH_INPUT,
+ F_CRM_THROTTLE_MAX, F_CRM_THROTTLE_MODE, F_CRM_USER, F_CRM_VERSION,
+ F_ORIG, F_SEQ, F_SUBTYPE, F_TYPE, F_XML_TAGNAME, INFINITY, INFINITY_S,
+ MINUS_INFINITY_S, OFFLINESTATUS, ONLINESTATUS,
+ PCMK_XA_PROMOTED_MAX_LEGACY, PCMK_XA_PROMOTED_NODE_MAX_LEGACY,
+ PCMK_XA_TARGET_PATTERN, PCMK_XA_UNAME, PCMK_XE_PROMOTABLE_LEGACY,
+ SUPPORT_UPSTART, T_ATTRD, T_CRM, XML_ACL_ATTR_ATTRIBUTE,
+ XML_ACL_ATTR_KIND, XML_ACL_ATTR_REF, XML_ACL_ATTR_REFv1, XML_ACL_ATTR_TAG,
+ XML_ACL_ATTR_TAGv1, XML_ACL_ATTR_XPATH, XML_ACL_TAG_DENY,
+ XML_ACL_TAG_GROUP, XML_ACL_TAG_PERMISSION, XML_ACL_TAG_READ,
+ XML_ACL_TAG_ROLE, XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1,
+ XML_ACL_TAG_USER, XML_ACL_TAG_USERv1, XML_ACL_TAG_WRITE,
+ XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER, XML_ALERT_ATTR_PATH,
+ XML_ALERT_ATTR_REC_VALUE, XML_ALERT_ATTR_TIMEOUT,
+ XML_ALERT_ATTR_TSTAMP_FORMAT, XML_ATTR_CRM_VERSION, XML_ATTR_DC_UUID,
+ XML_ATTR_DESC, XML_ATTR_DIGEST, XML_ATTR_GENERATION,
+ XML_ATTR_GENERATION_ADMIN, XML_ATTR_HAVE_QUORUM, XML_ATTR_HAVE_WATCHDOG,
+ XML_ATTR_ID, XML_ATTR_IDREF, XML_ATTR_ID_LONG, XML_ATTR_NAME,
+ XML_ATTR_NUMUPDATES, XML_ATTR_OP, XML_ATTR_ORIGIN, XML_ATTR_QUORUM_PANIC,
+ XML_ATTR_REFERENCE, XML_ATTR_REQUEST, XML_ATTR_RESPONSE,
+ XML_ATTR_STONITH_DEVICES, XML_ATTR_STONITH_INDEX, XML_ATTR_STONITH_TARGET,
+ XML_ATTR_STONITH_TARGET_ATTRIBUTE, XML_ATTR_STONITH_TARGET_VALUE,
+ XML_ATTR_TE_NOWAIT, XML_ATTR_TE_TARGET_RC, XML_ATTR_TIMEOUT,
+ XML_ATTR_TRANSITION_KEY, XML_ATTR_TRANSITION_MAGIC, XML_ATTR_TSTAMP,
+ XML_ATTR_TYPE, XML_ATTR_UPDATE_CLIENT, XML_ATTR_UPDATE_ORIGIN,
+ XML_ATTR_UPDATE_USER, XML_ATTR_VALIDATION, XML_ATTR_VERSION,
+ XML_BOOLEAN_FALSE, XML_BOOLEAN_NO, XML_BOOLEAN_TRUE, XML_BOOLEAN_YES,
+ XML_CIB_ATTR_PRIORITY, XML_CIB_ATTR_SHUTDOWN, XML_CIB_ATTR_WRITTEN,
+ XML_CIB_TAG_ACLS, XML_CIB_TAG_ALERT, XML_CIB_TAG_ALERTS,
+ XML_CIB_TAG_ALERT_ATTR, XML_CIB_TAG_ALERT_FENCING,
+ XML_CIB_TAG_ALERT_NODES, XML_CIB_TAG_ALERT_RECIPIENT,
+ XML_CIB_TAG_ALERT_RESOURCES, XML_CIB_TAG_ALERT_SELECT,
+ XML_CIB_TAG_CONFIGURATION, XML_CIB_TAG_CONSTRAINTS, XML_CIB_TAG_CONTAINER,
+ XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_GENERATION_TUPPLE, XML_CIB_TAG_GROUP,
+ XML_CIB_TAG_INCARNATION, XML_CIB_TAG_LRM, XML_CIB_TAG_NODE,
+ XML_CIB_TAG_NODES, XML_CIB_TAG_NVPAIR, XML_CIB_TAG_OBJ_REF,
+ XML_CIB_TAG_OPCONFIG, XML_CIB_TAG_PROPSET, XML_CIB_TAG_RESOURCE,
+ XML_CIB_TAG_RESOURCES, XML_CIB_TAG_RSCCONFIG, XML_CIB_TAG_RSC_TEMPLATE,
+ XML_CIB_TAG_SECTION_ALL, XML_CIB_TAG_STATE, XML_CIB_TAG_STATUS,
+ XML_CIB_TAG_TAG, XML_CIB_TAG_TAGS, XML_CIB_TAG_TICKETS,
+ XML_CIB_TAG_TICKET_STATE, XML_COLOC_ATTR_INFLUENCE,
+ XML_COLOC_ATTR_NODE_ATTR, XML_COLOC_ATTR_SOURCE,
+ XML_COLOC_ATTR_SOURCE_INSTANCE, XML_COLOC_ATTR_SOURCE_ROLE,
+ XML_COLOC_ATTR_TARGET, XML_COLOC_ATTR_TARGET_INSTANCE,
+ XML_COLOC_ATTR_TARGET_ROLE, XML_CONFIG_ATTR_DC_DEADTIME,
+ XML_CONFIG_ATTR_ELECTION_FAIL, XML_CONFIG_ATTR_FENCE_REACTION,
+ XML_CONFIG_ATTR_FORCE_QUIT, XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT,
+ XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY, XML_CONFIG_ATTR_RECHECK,
+ XML_CONFIG_ATTR_SHUTDOWN_LOCK, XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT,
+ XML_CONS_ATTR_SYMMETRICAL, XML_CONS_TAG_RSC_DEPEND,
+ XML_CONS_TAG_RSC_LOCATION, XML_CONS_TAG_RSC_ORDER, XML_CONS_TAG_RSC_SET,
+ XML_CONS_TAG_RSC_TICKET, XML_CRM_TAG_PING, XML_DIFF_ATTR, XML_DIFF_CHANGE,
+ XML_DIFF_LIST, XML_DIFF_MARKER, XML_DIFF_OP, XML_DIFF_PATH,
+ XML_DIFF_POSITION, XML_DIFF_RESULT, XML_DIFF_VERSION, XML_DIFF_VSOURCE,
+ XML_DIFF_VTARGET, XML_EXPR_ATTR_ATTRIBUTE, XML_EXPR_ATTR_OPERATION,
+ XML_EXPR_ATTR_TYPE, XML_EXPR_ATTR_VALUE, XML_EXPR_ATTR_VALUE_SOURCE,
+ XML_FAILCIB_ATTR_ID, XML_FAILCIB_ATTR_OBJTYPE, XML_FAILCIB_ATTR_OP,
+ XML_FAILCIB_ATTR_REASON, XML_FAIL_TAG_CIB, XML_GRAPH_TAG_CRM_EVENT,
+ XML_GRAPH_TAG_DOWNED, XML_GRAPH_TAG_MAINTENANCE,
+ XML_GRAPH_TAG_PSEUDO_EVENT, XML_GRAPH_TAG_RSC_OP,
+ XML_LOCATION_ATTR_DISCOVERY, XML_LOC_ATTR_SOURCE,
+ XML_LOC_ATTR_SOURCE_PATTERN, XML_LRM_ATTR_CALLID,
+ XML_LRM_ATTR_EXIT_REASON, XML_LRM_ATTR_INTERVAL, XML_LRM_ATTR_INTERVAL_MS,
+ XML_LRM_ATTR_MIGRATE_SOURCE, XML_LRM_ATTR_MIGRATE_TARGET,
+ XML_LRM_ATTR_OPSTATUS, XML_LRM_ATTR_OP_DIGEST, XML_LRM_ATTR_OP_RESTART,
+ XML_LRM_ATTR_OP_SECURE, XML_LRM_ATTR_RC, XML_LRM_ATTR_RESTART_DIGEST,
+ XML_LRM_ATTR_ROUTER_NODE, XML_LRM_ATTR_RSCID, XML_LRM_ATTR_SECURE_DIGEST,
+ XML_LRM_ATTR_TARGET, XML_LRM_ATTR_TARGET_UUID, XML_LRM_ATTR_TASK,
+ XML_LRM_ATTR_TASK_KEY, XML_LRM_TAG_RESOURCE, XML_LRM_TAG_RESOURCES,
+ XML_LRM_TAG_RSC_OP, XML_NODE_ATTR_RSC_DISCOVERY, XML_NODE_IS_FENCED,
+ XML_NODE_IS_MAINTENANCE, XML_NODE_IS_REMOTE, XML_NVPAIR_ATTR_NAME,
+ XML_NVPAIR_ATTR_VALUE, XML_OP_ATTR_ALLOW_MIGRATE, XML_OP_ATTR_DIGESTS_ALL,
+ XML_OP_ATTR_DIGESTS_SECURE, XML_OP_ATTR_INTERVAL_ORIGIN,
+ XML_OP_ATTR_ON_FAIL, XML_OP_ATTR_PENDING, XML_OP_ATTR_START_DELAY,
+ XML_ORDER_ATTR_FIRST, XML_ORDER_ATTR_FIRST_ACTION, XML_ORDER_ATTR_KIND,
+ XML_ORDER_ATTR_THEN, XML_ORDER_ATTR_THEN_ACTION, XML_PING_ATTR_CRMDSTATE,
+ XML_PING_ATTR_PACEMAKERDSTATE, XML_PING_ATTR_PACEMAKERDSTATE_INIT,
+ XML_PING_ATTR_PACEMAKERDSTATE_REMOTE,
+ XML_PING_ATTR_PACEMAKERDSTATE_RUNNING,
+ XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE,
+ XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN,
+ XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS,
+ XML_PING_ATTR_PACEMAKERDSTATE_WAITPING, XML_PING_ATTR_STATUS,
+ XML_PING_ATTR_SYSFROM, XML_REMOTE_ATTR_RECONNECT_INTERVAL,
+ XML_RSC_ATTR_CLEAR_INTERVAL, XML_RSC_ATTR_CLEAR_OP,
+ XML_RSC_ATTR_CONTAINER, XML_RSC_ATTR_CRITICAL, XML_RSC_ATTR_INCARNATION,
+ XML_RSC_ATTR_INTERLEAVE, XML_RSC_ATTR_INTERNAL_RSC,
+ XML_RSC_ATTR_MAINTENANCE, XML_RSC_ATTR_MANAGED, XML_RSC_ATTR_MULTIPLE,
+ XML_RSC_ATTR_NOTIFY, XML_RSC_ATTR_ORDERED, XML_RSC_ATTR_PROMOTABLE,
+ XML_RSC_ATTR_REMOTE_NODE, XML_RSC_ATTR_REMOTE_RA_ADDR,
+ XML_RSC_ATTR_REMOTE_RA_PORT, XML_RSC_ATTR_REMOTE_RA_SERVER,
+ XML_RSC_ATTR_REQUIRES, XML_RSC_ATTR_RESTART, XML_RSC_ATTR_STICKINESS,
+ XML_RSC_ATTR_TARGET, XML_RSC_ATTR_TARGET_ROLE, XML_RSC_ATTR_UNIQUE,
+ XML_RSC_OP_LAST_CHANGE, XML_RSC_OP_T_EXEC, XML_RSC_OP_T_QUEUE,
+ XML_RULE_ATTR_BOOLEAN_OP, XML_RULE_ATTR_ROLE, XML_RULE_ATTR_SCORE,
+ XML_RULE_ATTR_SCORE_ATTRIBUTE, XML_TAG_ATTRS, XML_TAG_ATTR_SETS,
+ XML_TAG_CIB, XML_TAG_DIFF, XML_TAG_EXPRESSION, XML_TAG_FAILED,
+ XML_TAG_FENCING_LEVEL, XML_TAG_FENCING_TOPOLOGY, XML_TAG_GRAPH,
+ XML_TAG_META_SETS, XML_TAG_OPTIONS, XML_TAG_PARAM, XML_TAG_PARAMS,
+ XML_TAG_RESOURCE_REF, XML_TAG_RULE, XML_TAG_TRANSIENT_NODEATTRS,
+ XML_TAG_UTILIZATION, XML_TICKET_ATTR_LOSS_POLICY, and
+ XML_TICKET_ATTR_TICKET
+ + libcrmcommon: deprecate direct access to all members of pcmk_scheduler_t,
+ pcmk_tag_t, and pcmk_ticket_t
+ + libcrmcommon: deprecate pcmk_rsc_methods_t, pcmk_assignment_methods_t,
+ struct pe_action_s, struct pe_resource_s, struct resource_alloc_functions_s,
+ struct resource_object_functions_s, struct pe_node_s, and
+ struct pe_node_shared_s, including all their members
+ + libcrmcommon: deprecate enums action_fail_response, action_tasks,
+ expression_type, node_type, pcmk_rsc_flags, pcmk_scheduler_flags,
+ pe_action_flags, pe_discover_e, pe_obj_types, rsc_recovery_type, and
+ rsc_start_requirement, including all their values
+ + liblrmd: deprecate constants F_LRMD_ALERT, F_LRMD_ALERT_ID,
+ F_LRMD_ALERT_PATH, F_LRMD_CALLBACK_TOKEN, F_LRMD_CALLDATA, F_LRMD_CALLID,
+ F_LRMD_CALLOPTS, F_LRMD_CLASS, F_LRMD_CLIENTID, F_LRMD_CLIENTNAME,
+ F_LRMD_EXEC_RC, F_LRMD_IPC_CLIENT, F_LRMD_IPC_IPC_SERVER, F_LRMD_IPC_MSG,
+ F_LRMD_IPC_MSG_FLAGS, F_LRMD_IPC_MSG_ID, F_LRMD_IPC_OP, F_LRMD_IPC_USER,
+ F_LRMD_IS_IPC_PROVIDER, F_LRMD_OPERATION, F_LRMD_OP_STATUS, F_LRMD_ORIGIN,
+ F_LRMD_PROTOCOL_VERSION, F_LRMD_PROVIDER, F_LRMD_RC, F_LRMD_REMOTE_MSG_ID,
+ F_LRMD_REMOTE_MSG_TYPE, F_LRMD_RSC, F_LRMD_RSC_ACTION, F_LRMD_RSC_DELETED,
+ F_LRMD_RSC_EXEC_TIME, F_LRMD_RSC_EXIT_REASON, F_LRMD_RSC_ID,
+ F_LRMD_RSC_INTERVAL, F_LRMD_RSC_OUTPUT, F_LRMD_RSC_QUEUE_TIME,
+ F_LRMD_RSC_RCCHANGE_TIME, F_LRMD_RSC_RUN_TIME, F_LRMD_RSC_START_DELAY,
+ F_LRMD_RSC_USERDATA_STR, F_LRMD_TIMEOUT, F_LRMD_TYPE, F_LRMD_WATCHDOG,
+ T_LRMD, T_LRMD_IPC_PROXY, T_LRMD_NOTIFY, T_LRMD_REPLY, and T_LRMD_RSC_OP
+ + libpacemaker: distribute pacemaker.h header to allow high-level API usage
+ + libpe_rules: deprecate functions find_expression_type(),
+ pe_evaluate_rules(), pe_eval_expr(), pe_eval_rules(), pe_eval_subexpr(),
+ pe_expand_re_matches(), pe_test_expression(), and pe_test_rule()
+ + libpe_rules,libpe_status: move enum expression_type and globals
+ was_processing_error and was_processing_warning to libcrmcommon
+ + libpe_rules,libpe_status: deprecate role member of pe_op_eval_data
+ + libpe_rules,libpe_status: deprecate functions text2task(), fail2text(),
+ recovery2text(), role2text(), task2text(), and text2role()
+ + libpe_status: deprecate functions pe_find_node(), pe_pref(),
+ pe_rsc_is_anon_clone(), pe_rsc_is_bundled(), pe_rsc_is_clone(),
+ pe_rsc_is_unique_clone(),
+ + libpe_status: deprecate global resource_class_functions
+ + libstonithd: deprecate constants T_STONITH_NOTIFY_DISCONNECT,
+ T_STONITH_NOTIFY_FENCE, T_STONITH_NOTIFY_HISTORY, and
+ T_STONITH_NOTIFY_HISTORY_SYNCED
+
* Tue Dec 19 2023 Ken Gaillot <kgaillot@redhat.com> Pacemaker-2.1.7
- 1388 commits with 358 files changed, 23771 insertions(+), 17219 deletions(-)
@@ -1606,7 +2068,7 @@
+ resources: drop broken configdir parameter from ocf:pacemaker:controld
- For further details, see:
- https://wiki.clusterlabs.org/wiki/Pacemaker_2.0_Changes
+ https://projects.clusterlabs.org/w/projects/pacemaker/pacemaker_2.0_changes/
* Tue Nov 14 2017 Ken Gaillot <kgaillot@redhat.com> Pacemaker-1.1.18
diff --git a/GNUmakefile b/GNUmakefile
index 8cac498..21a46c6 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -34,8 +34,8 @@ build: init
# Pass option depending on whether automake has been run or not
USE_FILE = $(shell test -e rpm/Makefile || echo "-f Makefile.am")
-.PHONY: $(PACKAGE).spec chroot dirty export mock rc release rpm rpmlint srpm
-$(PACKAGE).spec chroot dirty export mock rc release rpm rpmlint srpm:
+.PHONY: $(PACKAGE).spec chroot dirty export mock rc release rpm srpm
+$(PACKAGE).spec chroot dirty export mock rc release rpm srpm:
$(MAKE) $(AM_MAKEFLAGS) -C rpm $(USE_FILE) "$@"
mock-% rpm-% spec-% srpm-%: FORCE
diff --git a/INSTALL.md b/INSTALL.md
index e03c594..9819aca 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -57,22 +57,23 @@ Also:
| RPM packages via "make rpm" | 4.11 or later | rpm | rpm | (n/a) |
| unit tests | 1.1.0 or later | libcmocka-devel | libcmocka-devel | libcmocka-dev |
-## Optional testing dependencies
-* procps and psmisc (if running cts-exec, cts-fencing, or CTS)
-* valgrind (if running CTS valgrind tests)
-* python3-systemd (if using CTS on cluster nodes running systemd)
+## Optional Testing Dependencies
+* procps and psmisc (if running cts-exec, cts-fencing, or CTS lab)
+* valgrind (if running valgrind tests in cts-cli, cts-scheduler, or CTS lab)
+* python3-dateutil and python3-systemd (if running CTS lab on cluster nodes
+ running systemd)
* nmap (if not specifying an IP address base)
-* oprofile (if running CTS profiling tests)
-* dlm (to log DLM debugging info after CTS tests)
+* oprofile (if running CTS lab profiling tests)
+* dlm (to log DLM debugging info after CTS lab tests)
* xmllint (to validate tool output in cts-cli)
-## Simple install
+## Simple Install
$ make && sudo make install
If GNU make is not your default make, use "gmake" instead.
-## Detailed install
+## Detailed Install
First, browse the build options that are available:
diff --git a/Makefile.am b/Makefile.am
index c3e39b9..b2d8d55 100644
--- a/Makefile.am
+++ b/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/README.markdown b/README.markdown
index 4342875..d74f23a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -72,5 +72,6 @@ See [CONTRIBUTING.md](https://github.com/ClusterLabs/pacemaker/blob/main/CONTRIB
* [ClusterLabs website](https://www.clusterlabs.org/)
* [Documentation](https://www.clusterlabs.org/pacemaker/doc/)
* [Issues/Bugs](https://bugs.clusterlabs.org/)
-* [Mailing lists](https://wiki.clusterlabs.org/wiki/Mailing_lists) for users and developers
-* [ClusterLabs IRC channel](https://wiki.clusterlabs.org/wiki/ClusterLabs_IRC_channel)
+* [Mailing lists](https://projects.clusterlabs.org/w/clusterlabs/clusterlabs_mailing_lists/)
+ for users and developers
+* [ClusterLabs IRC channel](https://projects.clusterlabs.org/w/clusterlabs/clusterlabs_irc_channel/)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..a008885
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,18 @@
+# Security Policy
+
+## Supported Versions
+
+Pacemaker's 2.1 and 3.0 release series are actively developed and receive
+security fixes.
+
+## Reporting a Vulnerability
+
+If you have a support contract with an operating system vendor such as Red Hat
+or SUSE, please submit potentially security-related reports via the vendor's
+usual method. Otherwise, please submit a report via:
+
+ https://github.com/ClusterLabs/pacemaker/security
+
+## Past Vulnerabilities
+
+See https://projects.clusterlabs.org/w/cluster_administration/cves/
diff --git a/agents/ocf/HealthCPU.in b/agents/ocf/HealthCPU.in
index 14e4b07..1a691a9 100755
--- a/agents/ocf/HealthCPU.in
+++ b/agents/ocf/HealthCPU.in
@@ -26,6 +26,7 @@
: ${OCF_FUNCTIONS:="${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs"}
. "${OCF_FUNCTIONS}"
: ${__OCF_ACTION:="$1"}
+: ${OCF_RESKEY_dampening:="30s"}
#######################################################################
@@ -196,9 +197,6 @@ fi
if [ -z "${OCF_RESKEY_yellow_limit}" ] ; then
OCF_RESKEY_yellow_limit=50
fi
-if [ -z "${OCF_RESKEY_dampening}" ]; then
- OCF_RESKEY_dampening="30s"
-fi
case "$__OCF_ACTION" in
meta-data) meta_data
diff --git a/agents/ocf/HealthIOWait.in b/agents/ocf/HealthIOWait.in
index ba7a17a..65ce901 100755
--- a/agents/ocf/HealthIOWait.in
+++ b/agents/ocf/HealthIOWait.in
@@ -19,6 +19,7 @@
: ${OCF_FUNCTIONS:="${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs"}
. "${OCF_FUNCTIONS}"
: ${__OCF_ACTION:="$1"}
+: ${OCF_RESKEY_dampening:="5s"}
#######################################################################
@@ -60,12 +61,22 @@ the #health-iowait will go red if the %iowait of the CPU get higher than 15%.
<content type="integer" default="15"/>
</parameter>
+<parameter name="dampening" reloadable="1">
+<longdesc lang="en">
+The time to wait (dampening) in seconds for further changes before writing
+</longdesc>
+<shortdesc lang="en">The time to wait (dampening) in seconds for further changes
+before writing</shortdesc>
+<content type="string" default="5s"/>
+</parameter>
+
</parameters>
<actions>
<action name="start" timeout="10s" />
<action name="stop" timeout="10s" />
<action name="monitor" timeout="10s" interval="10s" start-delay="0s" />
+<action name="reload-agent" timeout="20s" />
<action name="meta-data" timeout="5s" />
<action name="validate-all" timeout="10s" depth="0" />
</actions>
@@ -77,7 +88,7 @@ END
agent_usage() {
cat <<END
-usage: $0 {start|stop|monitor|validate-all|meta-data}
+usage: $0 {start|stop|monitor|reload-agent|validate-all|meta-data}
Expects to have a fully populated OCF RA-compliant environment set.
END
@@ -109,22 +120,26 @@ agent_monitor() {
# echo $OCF_RESKEY_yellow_limit
if [ $WAIT -gt ${OCF_RESKEY_red_limit} ] ; then
# echo "System state RED!"
- attrd_updater -n "#health-iowait" -U "red" -d "5s"
+ attrd_updater -n "#health-iowait" -B "red" -d "${OCF_RESKEY_dampening}"
return $OCF_SUCCESS
fi
if [ $WAIT -gt ${OCF_RESKEY_yellow_limit} ] ; then
# echo "System state yellow."
- attrd_updater -n "#health-iowait" -U "yellow" -d "5s"
+ attrd_updater -n "#health-iowait" -B "yellow" -d "${OCF_RESKEY_dampening}"
else
# echo "System state green."
- attrd_updater -n "#health-iowait" -U "green" -d "5s"
+ attrd_updater -n "#health-iowait" -B "green" -d "${OCF_RESKEY_dampening}"
fi
return $OCF_SUCCESS
fi
return $OCF_NOT_RUNNING
}
+agent_reload_agent() {
+ return $OCF_SUCCESS
+}
+
is_integer() {
case "$1" in
""|*[0-9]*) return 0 ;;
@@ -181,7 +196,8 @@ meta-data) meta_data
;;
start) agent_start;;
stop) agent_stop;;
-monitor) agent_monitor;;
+monitor) agent_validate && agent_monitor;;
+reload-agent) agent_reload_agent;;
validate-all) agent_validate;;
usage|help) agent_usage
exit $OCF_SUCCESS
diff --git a/agents/ocf/HealthSMART.in b/agents/ocf/HealthSMART.in
index b2f37de..f688f73 100755
--- a/agents/ocf/HealthSMART.in
+++ b/agents/ocf/HealthSMART.in
@@ -80,7 +80,7 @@ The device type(s) to assume for the drive(s) being tested as a SPACE separated
<parameter name="temp_lower_limit" reloadable="1">
<longdesc lang="en">
-Lower limit of the temperature in deg C of the drive(s). Below this limit the status will be red.
+Lower limit of the temperature in deg C of the drive(s). Below this limit the status of #health-smart will be red.
</longdesc>
<shortdesc lang="en">Lower limit for the red smart attribute</shortdesc>
<content type="string" default="0"/>
@@ -116,7 +116,7 @@ The path to the smartctl program, used for querying device health.
The time to wait (dampening) for further changes to occur
</longdesc>
<shortdesc lang="en">Dampening interval</shortdesc>
-<content type="integer" default="5s"/>
+<content type="string" default="5s"/>
</parameter>
</parameters>
@@ -137,28 +137,30 @@ END
check_temperature() {
- if [ $1 -lt ${lower_red_limit} ] ; then
- ocf_log info "Drive ${DRIVE} ${DEVICE} too cold: ${1} C"
- attrd_updater -n "#health-smart" -B "red" -d "${OCF_RESKEY_dampen}"
- return 1
- fi
+ if [ -n "$1" ]; then
+ if [ $1 -lt ${lower_red_limit} ] ; then
+ ocf_log info "Drive ${DRIVE} ${DEVICE} too cold: ${1} C"
+ attrd_updater -n "#health-smart" -B "red" -d "${OCF_RESKEY_dampen}"
+ return 1
+ fi
- if [ $1 -gt ${upper_red_limit} ] ; then
- ocf_log info "Drive ${DRIVE} ${DEVICE} too hot: ${1} C"
- attrd_updater -n "#health-smart" -B "red" -d "${OCF_RESKEY_dampen}"
- return 1
- fi
+ if [ $1 -gt ${upper_red_limit} ] ; then
+ ocf_log info "Drive ${DRIVE} ${DEVICE} too hot: ${1} C"
+ attrd_updater -n "#health-smart" -B "red" -d "${OCF_RESKEY_dampen}"
+ return 1
+ fi
- if [ $1 -lt ${lower_yellow_limit} ] ; then
- ocf_log info "Drive ${DRIVE} ${DEVICE} quite cold: ${1} C"
- attrd_updater -n "#health-smart" -B "yellow" -d "${OCF_RESKEY_dampen}"
- return 1
- fi
+ if [ $1 -lt ${lower_yellow_limit} ] ; then
+ ocf_log info "Drive ${DRIVE} ${DEVICE} quite cold: ${1} C"
+ attrd_updater -n "#health-smart" -B "yellow" -d "${OCF_RESKEY_dampen}"
+ return 1
+ fi
- if [ $1 -gt ${upper_yellow_limit} ] ; then
- ocf_log info "Drive ${DRIVE} ${DEVICE} quite hot: ${1} C"
- attrd_updater -n "#health-smart" -B "yellow" -d "${OCF_RESKEY_dampen}"
- return 1
+ if [ $1 -gt ${upper_yellow_limit} ] ; then
+ ocf_log info "Drive ${DRIVE} ${DEVICE} quite hot: ${1} C"
+ attrd_updater -n "#health-smart" -B "yellow" -d "${OCF_RESKEY_dampen}"
+ return 1
+ fi
fi
}
@@ -350,7 +352,7 @@ fi
case "$__OCF_ACTION" in
start) HealthSMART_start;;
stop) HealthSMART_stop;;
- monitor) HealthSMART_monitor;;
+ monitor) HealthSMART_validate && HealthSMART_monitor;;
validate-all) HealthSMART_validate;;
reload-agent) HealthSMART_reload_agent;;
meta-data)
diff --git a/agents/ocf/SysInfo.in b/agents/ocf/SysInfo.in
index 5c2c7c7..d3a18f9 100755
--- a/agents/ocf/SysInfo.in
+++ b/agents/ocf/SysInfo.in
@@ -160,9 +160,11 @@ UpdateStat() {
value="$*"
printf "%s:\t%s\n" "$name" "$value"
if [ "$__OCF_ACTION" = "start" ] ; then
- "${HA_SBIN_DIR}/attrd_updater" ${OCF_RESKEY_delay} -S status -n $name -B "$value"
+ "${HA_SBIN_DIR}/attrd_updater" -d ${OCF_RESKEY_delay} -S status \
+ -n $name -B "$value"
else
- "${HA_SBIN_DIR}/attrd_updater" ${OCF_RESKEY_delay} -S status -n $name -v "$value"
+ "${HA_SBIN_DIR}/attrd_updater" -d ${OCF_RESKEY_delay} -S status \
+ -n $name -v "$value"
fi
}
diff --git a/agents/ocf/o2cb.in b/agents/ocf/o2cb.in
index f85d2f4..6db9d2b 100755
--- a/agents/ocf/o2cb.in
+++ b/agents/ocf/o2cb.in
@@ -366,7 +366,8 @@ meta_data() {
<resource-agent name="o2cb" version="@VERSION@">
<version>1.0</version>
<longdesc lang="en">
-This Resource Agent controls the userspace daemon needed by OCFS2.
+This Resource Agent controls the userspace daemon needed by OCFS2. This agent
+is deprecated and will be removed in Pacemaker 3.0.0.
</longdesc>
<shortdesc lang="en">OCFS2 daemon resource agent</shortdesc>
<parameters>
diff --git a/configure.ac b/configure.ac
index 6bff02e..78357bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
dnl
dnl autoconf for Pacemaker
dnl
-dnl Copyright 2009-2023 the Pacemaker project contributors
+dnl Copyright 2009-2024 the Pacemaker project contributors
dnl
dnl The version control history for this file may have further details.
dnl
@@ -533,6 +533,9 @@ AC_ARG_WITH([coverage],
yes_no_try "$with_coverage" "no"
with_coverage=$?
+AC_DEFINE_UNQUOTED([PCMK__WITH_COVERAGE], [$with_coverage], [Build with code coverage])
+AM_CONDITIONAL([BUILD_COVERAGE], [test $with_coverage -ne $DISABLED])
+
AC_ARG_WITH([sanitizers],
[AS_HELP_STRING([--with-sanitizers=...,...],
[enable SANitizer build, do *NOT* use for production. Only ASAN/UBSAN/TSAN are currently supported])],
@@ -838,6 +841,11 @@ AC_DEFINE_UNQUOTED([CRM_SCHEMA_DIRECTORY], ["$CRM_SCHEMA_DIRECTORY"],
[Location for the Pacemaker Relax-NG Schema])
AC_SUBST(CRM_SCHEMA_DIRECTORY)
+PCMK__REMOTE_SCHEMA_DIR="${localstatedir}/lib/pacemaker/schemas"
+AC_DEFINE_UNQUOTED([PCMK__REMOTE_SCHEMA_DIR], ["$PCMK__REMOTE_SCHEMA_DIR"],
+ [Location to store Relax-NG Schema files on remote nodes])
+AC_SUBST(PCMK__REMOTE_SCHEMA_DIR)
+
CRM_CORE_DIR="${localstatedir}/lib/pacemaker/cores"
AC_DEFINE_UNQUOTED([CRM_CORE_DIR], ["$CRM_CORE_DIR"],
[Directory Pacemaker daemons should change to (without systemd, core files will go here)])
@@ -1195,26 +1203,24 @@ AC_CHECK_DECLS([assert_float_equal], [], [], [[
#include <cmocka.h>
]])
-cc_temp_flags "$CFLAGS -Wl,--wrap=uname"
-
-WRAPPABLE_UNAME="no"
-
-AC_MSG_CHECKING([if uname() can be wrapped])
-AC_RUN_IFELSE([AC_LANG_SOURCE([[
-#include <sys/utsname.h>
-int __wrap_uname(struct utsname *buf) {
-return 100;
-}
-int main(int argc, char **argv) {
-struct utsname x;
-return uname(&x) == 100 ? 0 : 1;
-}
-]])],
- [ WRAPPABLE_UNAME="yes" ], [ WRAPPABLE_UNAME="no"])
-AC_MSG_RESULT([$WRAPPABLE_UNAME])
-AM_CONDITIONAL([WRAPPABLE_UNAME], [test x"$WRAPPABLE_UNAME" = x"yes"])
+dnl ========================================================================
+dnl Byte size
+dnl ========================================================================
-cc_restore_flags
+# Compile-time assert hack
+# https://jonjagger.blogspot.com/2017/07/compile-time-assertions-in-c.html
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <limits.h>]],
+ [[
+ switch (0) {
+ case 0:
+ case (CHAR_BIT == 8):
+ break;
+ }
+ ]])],
+ [],
+ [AC_MSG_FAILURE(m4_normalize([Pacemaker is not supported on
+ platforms where char is not 8
+ bits]))])
dnl ========================================================================
dnl Structures
@@ -2086,7 +2092,7 @@ CONFIG_FILES_EXEC([agents/ocf/ClusterMon],
[tools/crm_standby],
[tools/cibsecret],
[tools/pcmk_simtimes],
- [xml/version-diff.sh])
+ [xml/rng-helper])
dnl Other files we output
AC_CONFIG_FILES(Makefile \
@@ -2137,6 +2143,9 @@ AC_CONFIG_FILES(Makefile \
lib/Makefile \
lib/cib/Makefile \
lib/cluster/Makefile \
+ lib/cluster/tests/Makefile \
+ lib/cluster/tests/cluster/Makefile \
+ lib/cluster/tests/cpg/Makefile \
lib/common/Makefile \
lib/common/tests/Makefile \
lib/common/tests/acl/Makefile \
@@ -2148,11 +2157,17 @@ AC_CONFIG_FILES(Makefile \
lib/common/tests/io/Makefile \
lib/common/tests/iso8601/Makefile \
lib/common/tests/lists/Makefile \
+ lib/common/tests/nodes/Makefile \
lib/common/tests/nvpair/Makefile \
lib/common/tests/options/Makefile \
lib/common/tests/output/Makefile \
+ lib/common/tests/probes/Makefile \
lib/common/tests/procfs/Makefile \
+ lib/common/tests/resources/Makefile \
lib/common/tests/results/Makefile \
+ lib/common/tests/rules/Makefile \
+ lib/common/tests/scheduler/Makefile \
+ lib/common/tests/schemas/Makefile \
lib/common/tests/scores/Makefile \
lib/common/tests/strings/Makefile \
lib/common/tests/utils/Makefile \
@@ -2163,6 +2178,9 @@ AC_CONFIG_FILES(Makefile \
lib/libpacemaker.pc \
lib/lrmd/Makefile \
lib/pacemaker/Makefile \
+ lib/pacemaker/tests/Makefile \
+ lib/pacemaker/tests/pcmk_resource/Makefile \
+ lib/pacemaker/tests/pcmk_ticket/Makefile \
lib/pacemaker.pc \
lib/pacemaker-cib.pc \
lib/pacemaker-cluster.pc \
@@ -2174,7 +2192,6 @@ AC_CONFIG_FILES(Makefile \
lib/pengine/Makefile \
lib/pengine/tests/Makefile \
lib/pengine/tests/native/Makefile \
- lib/pengine/tests/rules/Makefile \
lib/pengine/tests/status/Makefile \
lib/pengine/tests/unpack/Makefile \
lib/pengine/tests/utils/Makefile \
diff --git a/cts/README.md b/cts/README.md
index cbf319a..595268d 100644
--- a/cts/README.md
+++ b/cts/README.md
@@ -103,6 +103,15 @@ CTS includes:
*must* match the hosts' names as returned by `uname -n`; they do not have to
match the machines' fully qualified domain names.
+* Optionally, configure the exerciser as a log aggregator, using something like
+ `rsyslog` log forwarding. If aggregation is detected, the exerciser will look
+ for new messages locally instead of requesting them repeatedly from cluster
+ nodes.
+ * Currently, `/var/log/messages` on the exerciser is the only supported log
+ destination. Further, if it's specified explicitly on the command line as
+ the log file, then CTS lab will not check for aggregation.
+ * CTS lab does not currently detect systemd journal log aggregation.
+
### Run
diff --git a/cts/cli/regression.access_render.exp b/cts/cli/regression.access_render.exp
index 37f093d..d818b65 100644
--- a/cts/cli/regression.access_render.exp
+++ b/cts/cli/regression.access_render.exp
@@ -1,22 +1,22 @@
Created new pacemaker configuration
-Setting up shadow instance
-A new shadow instance was created. To begin using it paste the following into your shell:
- CIB_shadow=cts-cli ; export CIB_shadow
+A new shadow instance was created. To begin using it, enter the following into your shell:
+ export CIB_shadow=cts-cli
=#=#=#= Begin test: Configure some ACLs =#=#=#=
=#=#=#= Current cib after: Configure some ACLs =#=#=#=
-<cib epoch="1" num_updates="0" admin_epoch="0">
+<cib epoch="2" num_updates="0" admin_epoch="0">
<configuration>
<crm_config/>
<nodes/>
<resources/>
<constraints/>
<acls>
- <acl_role id="role-deny-acls">
+ <acl_role id="role-deny-acls-write-resources">
<acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ <acl_permission id="write-resources" kind="write" xpath="/cib/configuration/resources"/>
<acl_permission id="read-rest" kind="read" xpath="/cib"/>
</acl_role>
<acl_target id="tony">
- <role id="role-deny-acls"/>
+ <role id="role-deny-acls-write-resources"/>
</acl_target>
</acls>
</configuration>
@@ -26,7 +26,7 @@ A new shadow instance was created. To begin using it paste the following into y
* Passed: cibadmin - Configure some ACLs
=#=#=#= Begin test: Enable ACLs =#=#=#=
=#=#=#= Current cib after: Enable ACLs =#=#=#=
-<cib epoch="2" num_updates="0" admin_epoch="0">
+<cib epoch="3" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -37,12 +37,13 @@ A new shadow instance was created. To begin using it paste the following into y
<resources/>
<constraints/>
<acls>
- <acl_role id="role-deny-acls">
+ <acl_role id="role-deny-acls-write-resources">
<acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ <acl_permission id="write-resources" kind="write" xpath="/cib/configuration/resources"/>
<acl_permission id="read-rest" kind="read" xpath="/cib"/>
</acl_role>
<acl_target id="tony">
- <role id="role-deny-acls"/>
+ <role id="role-deny-acls-write-resources"/>
</acl_target>
</acls>
</configuration>
@@ -52,7 +53,7 @@ A new shadow instance was created. To begin using it paste the following into y
* Passed: crm_attribute - Enable ACLs
=#=#=#= Begin test: An instance of ACLs render (into color) =#=#=#=
<!-- ACLs as evaluated for user tony -->
-\x1b[34m<cib epoch="2" num_updates="0" admin_epoch="0">
+\x1b[34m<cib epoch="3" num_updates="0" admin_epoch="0">
\x1b[34m<configuration>
\x1b[34m<crm_config>
\x1b[34m<cluster_property_set id="cib-bootstrap-options">
@@ -60,15 +61,16 @@ A new shadow instance was created. To begin using it paste the following into y
\x1b[34m</cluster_property_set>
\x1b[34m</crm_config>
\x1b[34m<nodes/>
- \x1b[34m<resources/>
+ \x1b[32m<resources/>
\x1b[34m<constraints/>
\x1b[31m<acls>
- \x1b[31m<acl_role id="role-deny-acls">
+ \x1b[31m<acl_role id="role-deny-acls-write-resources">
\x1b[31m<acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ \x1b[31m<acl_permission id="write-resources" kind="write" xpath="/cib/configuration/resources"/>
\x1b[31m<acl_permission id="read-rest" kind="read" xpath="/cib"/>
\x1b[31m</acl_role>
\x1b[31m<acl_target id="tony">
- \x1b[31m<role id="role-deny-acls"/>
+ \x1b[31m<role id="role-deny-acls-write-resources"/>
\x1b[31m</acl_target>
\x1b[31m</acls>
\x1b[34m</configuration>
@@ -78,7 +80,7 @@ A new shadow instance was created. To begin using it paste the following into y
* Passed: cibadmin - An instance of ACLs render (into color)
=#=#=#= Begin test: An instance of ACLs render (into namespacing) =#=#=#=
<!-- ACLs as evaluated for user tony -->
-<pcmk-access-readable:cib epoch="2" num_updates="0" admin_epoch="0" xmlns:pcmk-access-readable="http://clusterlabs.org/ns/pacemaker/access/readable" xmlns:pcmk-access-denied="http://clusterlabs.org/ns/pacemaker/access/denied">
+<pcmk-access-readable:cib epoch="3" num_updates="0" admin_epoch="0" xmlns:pcmk-access-writable="http://clusterlabs.org/ns/pacemaker/access/writable" xmlns:pcmk-access-readable="http://clusterlabs.org/ns/pacemaker/access/readable" xmlns:pcmk-access-denied="http://clusterlabs.org/ns/pacemaker/access/denied">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -86,15 +88,16 @@ A new shadow instance was created. To begin using it paste the following into y
</cluster_property_set>
</crm_config>
<nodes/>
- <resources/>
+ <pcmk-access-writable:resources/>
<constraints/>
<pcmk-access-denied:acls>
- <acl_role id="role-deny-acls">
+ <acl_role id="role-deny-acls-write-resources">
<acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ <acl_permission id="write-resources" kind="write" xpath="/cib/configuration/resources"/>
<acl_permission id="read-rest" kind="read" xpath="/cib"/>
</acl_role>
<acl_target id="tony">
- <role id="role-deny-acls"/>
+ <role id="role-deny-acls-write-resources"/>
</acl_target>
</pcmk-access-denied:acls>
</configuration>
@@ -105,7 +108,7 @@ A new shadow instance was created. To begin using it paste the following into y
=#=#=#= Begin test: An instance of ACLs render (into text) =#=#=#=
<!-- ACLs as evaluated for user tony -->
vvv---[ READABLE ]---vvv
-<cib epoch="2" num_updates="0" admin_epoch="0">
+<cib epoch="3" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -113,17 +116,20 @@ vvv---[ READABLE ]---vvv
</cluster_property_set>
</crm_config>
<nodes/>
+
+ vvv---[ WRITABLE ]---vvv
<resources/>
<constraints/>
vvv---[ ~DENIED~ ]---vvv
<acls>
- <acl_role id="role-deny-acls">
+ <acl_role id="role-deny-acls-write-resources">
<acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ <acl_permission id="write-resources" kind="write" xpath="/cib/configuration/resources"/>
<acl_permission id="read-rest" kind="read" xpath="/cib"/>
</acl_role>
<acl_target id="tony">
- <role id="role-deny-acls"/>
+ <role id="role-deny-acls-write-resources"/>
</acl_target>
</acls>
</configuration>
diff --git a/cts/cli/regression.acls.exp b/cts/cli/regression.acls.exp
index c0b0c4f..1822328 100644
--- a/cts/cli/regression.acls.exp
+++ b/cts/cli/regression.acls.exp
@@ -30,8 +30,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -87,8 +87,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -145,8 +145,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -203,8 +203,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -264,8 +264,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -328,8 +328,8 @@ A new shadow instance was created. To begin using it, enter the following into y
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -372,14 +372,9 @@ crm_attribute: Error performing operation: Permission denied
=#=#=#= End test: unknownguy: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
* Passed: crm_attribute - unknownguy: Set stonith-enabled
=#=#=#= Begin test: unknownguy: Create a resource =#=#=#=
-pcmk__check_acl trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id]
-pcmk__apply_creation_acl trace: Creation of <primitive> scaffolding with id="<unset>" is implicitly allowed
+pcmk__check_acl trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id='dummy']
+pcmk__apply_creation_acl trace: ACLs disallow creation of <primitive> with id="dummy"
Call failed: Permission denied
-<failed>
- <failed_update id="dummy" object_type="primitive" operation="cib_create" reason="Permission denied">
- <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
- </failed_update>
-</failed>
=#=#=#= End test: unknownguy: Create a resource - Insufficient privileges (4) =#=#=#=
* Passed: cibadmin - unknownguy: Create a resource
=#=#=#= Begin test: l33t-haxor: Query configuration =#=#=#=
@@ -433,8 +428,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -506,8 +501,8 @@ pcmk__apply_creation_acl trace: ACLs allow creation of <nvpair> with id="cib-bo
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -577,8 +572,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -643,8 +638,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -711,8 +706,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -801,8 +796,8 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -877,8 +872,8 @@ Stopped
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -951,8 +946,8 @@ Deleted 'dummy' option: id=dummy-meta_attributes-target-role name=target-role
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1028,8 +1023,8 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1154,8 +1149,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1229,8 +1224,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1303,8 +1298,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1377,8 +1372,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1451,8 +1446,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1522,8 +1517,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1589,8 +1584,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1656,8 +1651,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1723,8 +1718,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1790,8 +1785,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1857,8 +1852,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1924,8 +1919,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -1991,8 +1986,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -2058,8 +2053,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -2127,8 +2122,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -2196,8 +2191,8 @@ Call failed: Permission denied
</acl_user>
<acl_role id="observer">
<read id="observer-read-1" xpath="/cib"/>
- <write id="observer-write-1" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <write id="observer-write-2" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
+ <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<read id="admin-read-1" xpath="/cib"/>
@@ -2273,8 +2268,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2323,14 +2318,9 @@ crm_attribute: Error performing operation: Permission denied
=#=#=#= End test: unknownguy: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
* Passed: crm_attribute - unknownguy: Set stonith-enabled
=#=#=#= Begin test: unknownguy: Create a resource =#=#=#=
-pcmk__check_acl trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id]
-pcmk__apply_creation_acl trace: Creation of <primitive> scaffolding with id="<unset>" is implicitly allowed
+pcmk__check_acl trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id='dummy']
+pcmk__apply_creation_acl trace: ACLs disallow creation of <primitive> with id="dummy"
Call failed: Permission denied
-<failed>
- <failed_update id="dummy" object_type="primitive" operation="cib_create" reason="Permission denied">
- <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
- </failed_update>
-</failed>
=#=#=#= End test: unknownguy: Create a resource - Insufficient privileges (4) =#=#=#=
* Passed: cibadmin - unknownguy: Create a resource
=#=#=#= Begin test: l33t-haxor: Query configuration =#=#=#=
@@ -2388,8 +2378,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2469,8 +2459,8 @@ crm_attribute: Error performing operation: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2549,8 +2539,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2624,8 +2614,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2701,8 +2691,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2800,8 +2790,8 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2885,8 +2875,8 @@ Stopped
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -2968,8 +2958,8 @@ Deleted 'dummy' option: id=dummy-meta_attributes-target-role name=target-role
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3054,8 +3044,8 @@ Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attribut
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3189,8 +3179,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3273,8 +3263,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3356,8 +3346,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3439,8 +3429,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3522,8 +3512,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3602,8 +3592,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3678,8 +3668,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3754,8 +3744,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3830,8 +3820,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3906,8 +3896,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -3982,8 +3972,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -4058,8 +4048,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -4134,8 +4124,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -4210,8 +4200,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -4288,8 +4278,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
@@ -4366,8 +4356,8 @@ Call failed: Permission denied
</acl_target>
<acl_role id="observer">
<acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
- <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name=&apos;stonith-enabled&apos;]"/>
- <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name=&apos;target-role&apos;]"/>
+ <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
+ <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
</acl_role>
<acl_role id="admin">
<acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
diff --git a/cts/cli/regression.daemons.exp b/cts/cli/regression.daemons.exp
index b34fba8..d530c4a 100644
--- a/cts/cli/regression.daemons.exp
+++ b/cts/cli/regression.daemons.exp
@@ -1,18 +1,31 @@
=#=#=#= Begin test: Get CIB manager metadata =#=#=#=
-<?xml version=""?>
<resource-agent name="pacemaker-based" version="">
- <version>1.1</version>
- <longdesc lang="en">Cluster options used by Pacemaker&apos;s Cluster Information Base manager</longdesc>
- <shortdesc lang="en">Cluster Information Base manager options</shortdesc>
+ <version>
+ 1.1
+ </version>
+ <longdesc lang="en">
+ Cluster options used by Pacemaker's Cluster Information Base manager
+ </longdesc>
+ <shortdesc lang="en">
+ Cluster Information Base manager options
+ </shortdesc>
<parameters>
<parameter name="enable-acl">
- <longdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</longdesc>
- <shortdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</shortdesc>
+ <longdesc lang="en">
+ Enable Access Control Lists (ACLs) for the CIB
+ </longdesc>
+ <shortdesc lang="en">
+ Enable Access Control Lists (ACLs) for the CIB
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="cluster-ipc-limit">
- <longdesc lang="en">Raise this if log has &quot;Evicting client&quot; messages for cluster daemon PIDs (a good value is the number of resources in the cluster multiplied by the number of nodes).</longdesc>
- <shortdesc lang="en">Maximum IPC message backlog before disconnecting a cluster daemon</shortdesc>
+ <longdesc lang="en">
+ 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).
+ </longdesc>
+ <shortdesc lang="en">
+ Maximum IPC message backlog before disconnecting a cluster daemon
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
</parameters>
@@ -20,247 +33,399 @@
=#=#=#= End test: Get CIB manager metadata - OK (0) =#=#=#=
* Passed: pacemaker-based - Get CIB manager metadata
=#=#=#= Begin test: Get controller metadata =#=#=#=
-<?xml version=""?>
<resource-agent name="pacemaker-controld" version="">
- <version>1.1</version>
- <longdesc lang="en">Cluster options used by Pacemaker&apos;s controller</longdesc>
- <shortdesc lang="en">Pacemaker controller options</shortdesc>
+ <version>
+ 1.1
+ </version>
+ <longdesc lang="en">
+ Cluster options used by Pacemaker's controller
+ </longdesc>
+ <shortdesc lang="en">
+ Pacemaker controller options
+ </shortdesc>
<parameters>
<parameter name="dc-version">
- <longdesc lang="en">Includes a hash which identifies the exact changeset the code was built from. Used for diagnostic purposes.</longdesc>
- <shortdesc lang="en">Pacemaker version on cluster node elected Designated Controller (DC)</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ Includes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.
+ </longdesc>
+ <shortdesc lang="en">
+ Pacemaker version on cluster node elected Designated Controller (DC)
+ </shortdesc>
+ <content type="string"/>
</parameter>
<parameter name="cluster-infrastructure">
- <longdesc lang="en">Used for informational and diagnostic purposes.</longdesc>
- <shortdesc lang="en">The messaging stack on which Pacemaker is currently running</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ Used for informational and diagnostic purposes.
+ </longdesc>
+ <shortdesc lang="en">
+ The messaging layer on which Pacemaker is currently running
+ </shortdesc>
+ <content type="string"/>
</parameter>
<parameter name="cluster-name">
- <longdesc lang="en">This optional value is mostly for users&apos; 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.</longdesc>
- <shortdesc lang="en">An arbitrary name for the cluster</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ An arbitrary name for the cluster
+ </shortdesc>
<content type="string"/>
</parameter>
<parameter name="dc-deadtime">
- <longdesc lang="en">The optimal value will depend on the speed and load of your network and the type of switches used.</longdesc>
- <shortdesc lang="en">How long to wait for a response from other nodes during start-up</shortdesc>
+ <longdesc lang="en">
+ The optimal value will depend on the speed and load of your network and the type of switches used.
+ </longdesc>
+ <shortdesc lang="en">
+ How long to wait for a response from other nodes during start-up
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="cluster-recheck-interval">
- <longdesc lang="en">Pacemaker is primarily event-driven, and looks ahead to know when to recheck cluster state for failure timeouts 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. Allowed values: Zero disables polling, while positive values are an interval in seconds(unless other units are specified, for example "5min")</longdesc>
- <shortdesc lang="en">Polling interval to recheck cluster state and evaluate rules with date specifications</shortdesc>
+ <longdesc lang="en">
+ 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").
+ </longdesc>
+ <shortdesc lang="en">
+ Polling interval to recheck cluster state and evaluate rules with date specifications
+ </shortdesc>
<content type="time" default=""/>
</parameter>
- <parameter name="load-threshold">
- <longdesc lang="en">The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit</longdesc>
- <shortdesc lang="en">Maximum amount of system load that should be used by cluster nodes</shortdesc>
- <content type="percentage" default=""/>
- </parameter>
- <parameter name="node-action-limit">
- <longdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</longdesc>
- <shortdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</shortdesc>
- <content type="integer" default=""/>
- </parameter>
<parameter name="fence-reaction">
- <longdesc lang="en">A cluster node may receive notification of its own fencing if fencing is misconfigured, or if fabric fencing is in use that doesn&apos;t cut cluster communication. Allowed values are &quot;stop&quot; to attempt to immediately stop Pacemaker and stay stopped, or &quot;panic&quot; to attempt to immediately reboot the local node, falling back to stop on failure.</longdesc>
- <shortdesc lang="en">How a cluster node should react if notified of its own fencing</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ 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. Allowed values: stop, panic
+ </longdesc>
+ <shortdesc lang="en">
+ How a cluster node should react if notified of its own fencing
+ </shortdesc>
+ <content type="select" default="">
+ <option value="stop"/>
+ <option value="panic"/>
+ </content>
</parameter>
<parameter name="election-timeout">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only ***</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only ***
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="shutdown-escalation">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only ***</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only ***
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="join-integration-timeout">
- <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only ***</shortdesc>
+ <longdesc lang="en">
+ If you need to adjust this value, it probably indicates the presence of a bug.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only ***
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="join-finalization-timeout">
- <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only ***</shortdesc>
+ <longdesc lang="en">
+ If you need to adjust this value, it probably indicates the presence of a bug.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only ***
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="transition-delay">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only *** Enabling this option will slow down cluster recovery under all conditions</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Enabling this option will slow down cluster recovery under all conditions
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="stonith-watchdog-timeout">
- <longdesc lang="en">If this is set to a positive value, lost nodes are assumed to self-fence 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.</longdesc>
- <shortdesc lang="en">How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="stonith-max-attempts">
- <longdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</longdesc>
- <shortdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</shortdesc>
+ <longdesc lang="en">
+ How many times fencing can fail before it will no longer be immediately re-attempted on a target
+ </longdesc>
+ <shortdesc lang="en">
+ How many times fencing can fail before it will no longer be immediately re-attempted on a target
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
- <parameter name="no-quorum-policy">
- <longdesc lang="en">What to do when the cluster does not have quorum Allowed values: stop, freeze, ignore, demote, suicide</longdesc>
- <shortdesc lang="en">What to do when the cluster does not have quorum</shortdesc>
- <content type="select" default="">
- <option value="stop" />
- <option value="freeze" />
- <option value="ignore" />
- <option value="demote" />
- <option value="suicide" />
- </content>
- </parameter>
- <parameter name="shutdown-lock">
- <longdesc lang="en">When true, resources active on a node when it is cleanly shut down are kept &quot;locked&quot; 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.</longdesc>
- <shortdesc lang="en">Whether to lock resources to a cleanly shut down node</shortdesc>
- <content type="boolean" default=""/>
- </parameter>
- <parameter name="shutdown-lock-limit">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">Do not lock resources to a cleanly shut down node longer than this</shortdesc>
- <content type="time" default=""/>
+ <parameter name="load-threshold">
+ <longdesc lang="en">
+ The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit
+ </longdesc>
+ <shortdesc lang="en">
+ Maximum amount of system load that should be used by cluster nodes
+ </shortdesc>
+ <content type="percentage" default=""/>
</parameter>
- <parameter name="node-pending-timeout">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">How long to wait for a node that has joined the cluster to join the controller process group</shortdesc>
- <content type="time" default=""/>
+ <parameter name="node-action-limit">
+ <longdesc lang="en">
+ Maximum number of jobs that can be scheduled per node (defaults to 2x cores)
+ </longdesc>
+ <shortdesc lang="en">
+ Maximum number of jobs that can be scheduled per node (defaults to 2x cores)
+ </shortdesc>
+ <content type="integer" default=""/>
</parameter>
</parameters>
</resource-agent>
=#=#=#= End test: Get controller metadata - OK (0) =#=#=#=
* Passed: pacemaker-controld - Get controller metadata
=#=#=#= Begin test: Get fencer metadata =#=#=#=
-<?xml version=""?>
<resource-agent name="pacemaker-fenced" version="">
- <version>1.1</version>
- <longdesc lang="en">Instance attributes available for all &quot;stonith&quot;-class resources and used by Pacemaker&apos;s fence daemon, formerly known as stonithd</longdesc>
- <shortdesc lang="en">Instance attributes available for all &quot;stonith&quot;-class resources</shortdesc>
+ <version>
+ 1.1
+ </version>
+ <longdesc lang="en">
+ Instance attributes available for all "stonith"-class resources and used by Pacemaker's fence daemon, formerly known as stonithd
+ </longdesc>
+ <shortdesc lang="en">
+ Instance attributes available for all "stonith"-class resources
+ </shortdesc>
<parameters>
<parameter name="pcmk_host_argument">
- <longdesc lang="en">some devices do not support the standard &apos;port&apos; 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.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate parameter to supply instead of &apos;port&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate parameter to supply instead of 'port'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_host_map">
- <longdesc lang="en">Eg. node1:1;node2:2,3 would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2</longdesc>
- <shortdesc lang="en">A mapping of host names to ports numbers for devices that do not support host names.</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ For example, "node1:1;node2:2,3" would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2.
+ </longdesc>
+ <shortdesc lang="en">
+ A mapping of node names to port numbers for devices that do not support node names.
+ </shortdesc>
+ <content type="string"/>
</parameter>
<parameter name="pcmk_host_list">
- <longdesc lang="en">A list of machines controlled by this device (Optional unless pcmk_host_list=static-list)</longdesc>
- <shortdesc lang="en">Eg. node1,node2,node3</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Nodes targeted by this device
+ </shortdesc>
+ <content type="string"/>
</parameter>
<parameter name="pcmk_host_check">
- <longdesc lang="en">Allowed values: dynamic-list (query the device via the &apos;list&apos; command), static-list (check the pcmk_host_list attribute), status (query the device via the &apos;status&apos; command), none (assume every device can fence every machine)</longdesc>
- <shortdesc lang="en">How to determine which machines are controlled by the device.</shortdesc>
- <content type="string" default=""/>
+ <longdesc lang="en">
+ 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" Allowed values: dynamic-list, static-list, status, none
+ </longdesc>
+ <shortdesc lang="en">
+ How to determine which nodes can be targeted by the device
+ </shortdesc>
+ <content type="select">
+ <option value="dynamic-list"/>
+ <option value="static-list"/>
+ <option value="status"/>
+ <option value="none"/>
+ </content>
</parameter>
<parameter name="pcmk_delay_max">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">Enable a base delay for fencing actions and specify base delay value.</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Enable a delay of no more than the time specified before executing fencing actions.
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_delay_base">
- <longdesc lang="en">This enables a static delay for fencing actions, which can help avoid &quot;death matches&quot; 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, &quot;node1:1s;node2:5&quot;) to set a different value per target.</longdesc>
- <shortdesc lang="en">Enable a base delay for fencing actions and specify base delay value.</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Enable a base delay for fencing actions and specify base delay value.
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_action_limit">
- <longdesc lang="en">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. -1 is unlimited.</longdesc>
- <shortdesc lang="en">The maximum number of actions can be performed in parallel on this device</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ The maximum number of actions can be performed in parallel on this device
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_reboot_action">
- <longdesc lang="en">Some devices do not support the standard commands or may provide additional ones.\nUse this to specify an alternate, device-specific, command that implements the &apos;reboot&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;reboot&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'reboot'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_reboot_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.Use this to specify an alternate, device-specific, timeout for &apos;reboot&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for reboot actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'reboot' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'reboot' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_reboot_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;reboot&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;reboot&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'reboot' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_off_action">
- <longdesc lang="en">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 &apos;off&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;off&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'off'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_off_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.Use this to specify an alternate, device-specific, timeout for &apos;off&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for off actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'off' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'off' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_off_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;off&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;off&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'off' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_on_action">
- <longdesc lang="en">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 &apos;on&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;on&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'on'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_on_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.Use this to specify an alternate, device-specific, timeout for &apos;on&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for on actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'on' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'on' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_on_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;on&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;on&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'on' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_list_action">
- <longdesc lang="en">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 &apos;list&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;list&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'list'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_list_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.Use this to specify an alternate, device-specific, timeout for &apos;list&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for list actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'list' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'list' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_list_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;list&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;list&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'list' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_monitor_action">
- <longdesc lang="en">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 &apos;monitor&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;monitor&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'monitor'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_monitor_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.\nUse this to specify an alternate, device-specific, timeout for &apos;monitor&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for monitor actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'monitor' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'monitor' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_monitor_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;monitor&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;monitor&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'monitor' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pcmk_status_action">
- <longdesc lang="en">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 &apos;status&apos; action.</longdesc>
- <shortdesc lang="en">Advanced use only: An alternate command to run instead of &apos;status&apos;</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** An alternate command to run instead of 'status'
+ </shortdesc>
<content type="string" default=""/>
</parameter>
<parameter name="pcmk_status_timeout">
- <longdesc lang="en">Some devices need much more/less time to complete than normal.Use this to specify an alternate, device-specific, timeout for &apos;status&apos; actions.</longdesc>
- <shortdesc lang="en">Advanced use only: Specify an alternate timeout to use for status actions instead of stonith-timeout</shortdesc>
+ <longdesc lang="en">
+ Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'status' actions.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Specify an alternate timeout to use for 'status' actions instead of stonith-timeout
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="pcmk_status_retries">
- <longdesc lang="en">Some devices do not support multiple connections. Operations may &apos;fail&apos; if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining. Use this option to alter the number of times Pacemaker retries &apos;status&apos; actions before giving up.</longdesc>
- <shortdesc lang="en">Advanced use only: The maximum number of times to retry the &apos;status&apos; command within the timeout period</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** The maximum number of times to try the 'status' command within the timeout period
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
</parameters>
@@ -268,186 +433,315 @@
=#=#=#= End test: Get fencer metadata - OK (0) =#=#=#=
* Passed: pacemaker-fenced - Get fencer metadata
=#=#=#= Begin test: Get scheduler metadata =#=#=#=
-<?xml version=""?>
<resource-agent name="pacemaker-schedulerd" version="">
- <version>1.1</version>
- <longdesc lang="en">Cluster options used by Pacemaker&apos;s scheduler</longdesc>
- <shortdesc lang="en">Pacemaker scheduler options</shortdesc>
+ <version>
+ 1.1
+ </version>
+ <longdesc lang="en">
+ Cluster options used by Pacemaker's scheduler
+ </longdesc>
+ <shortdesc lang="en">
+ Pacemaker scheduler options
+ </shortdesc>
<parameters>
<parameter name="no-quorum-policy">
- <longdesc lang="en">What to do when the cluster does not have quorum Allowed values: stop, freeze, ignore, demote, suicide</longdesc>
- <shortdesc lang="en">What to do when the cluster does not have quorum</shortdesc>
+ <longdesc lang="en">
+ What to do when the cluster does not have quorum Allowed values: stop, freeze, ignore, demote, suicide
+ </longdesc>
+ <shortdesc lang="en">
+ What to do when the cluster does not have quorum
+ </shortdesc>
<content type="select" default="">
- <option value="stop" />
- <option value="freeze" />
- <option value="ignore" />
- <option value="demote" />
- <option value="suicide" />
+ <option value="stop"/>
+ <option value="freeze"/>
+ <option value="ignore"/>
+ <option value="demote"/>
+ <option value="suicide"/>
</content>
</parameter>
+ <parameter name="shutdown-lock">
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Whether to lock resources to a cleanly shut down node
+ </shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="shutdown-lock-limit">
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Do not lock resources to a cleanly shut down node longer than this
+ </shortdesc>
+ <content type="time" default=""/>
+ </parameter>
<parameter name="symmetric-cluster">
- <longdesc lang="en">Whether resources can run on any node by default</longdesc>
- <shortdesc lang="en">Whether resources can run on any node by default</shortdesc>
+ <longdesc lang="en">
+ Whether resources can run on any node by default
+ </longdesc>
+ <shortdesc lang="en">
+ Whether resources can run on any node by default
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="maintenance-mode">
- <longdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</longdesc>
- <shortdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</shortdesc>
+ <longdesc lang="en">
+ Whether the cluster should refrain from monitoring, starting, and stopping resources
+ </longdesc>
+ <shortdesc lang="en">
+ Whether the cluster should refrain from monitoring, starting, and stopping resources
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="start-failure-is-fatal">
- <longdesc lang="en">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&apos;s fail count against its migration-threshold.</longdesc>
- <shortdesc lang="en">Whether a start failure should prevent a resource from being recovered on the same node</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Whether a start failure should prevent a resource from being recovered on the same node
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="enable-startup-probes">
- <longdesc lang="en">Whether the cluster should check for active resources during start-up</longdesc>
- <shortdesc lang="en">Whether the cluster should check for active resources during start-up</shortdesc>
+ <longdesc lang="en">
+ Whether the cluster should check for active resources during start-up
+ </longdesc>
+ <shortdesc lang="en">
+ Whether the cluster should check for active resources during start-up
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
- <parameter name="shutdown-lock">
- <longdesc lang="en">When true, resources active on a node when it is cleanly shut down are kept &quot;locked&quot; 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.</longdesc>
- <shortdesc lang="en">Whether to lock resources to a cleanly shut down node</shortdesc>
- <content type="boolean" default=""/>
- </parameter>
- <parameter name="shutdown-lock-limit">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">Do not lock resources to a cleanly shut down node longer than this</shortdesc>
- <content type="time" default=""/>
- </parameter>
<parameter name="stonith-enabled">
- <longdesc lang="en">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 &quot;split-brain&quot; situation, potentially leading to data loss and/or service unavailability.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only *** Whether nodes may be fenced as part of recovery</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Whether nodes may be fenced as part of recovery
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="stonith-action">
- <longdesc lang="en">Action to send to fence device when a node needs to be fenced (&quot;poweroff&quot; is a deprecated alias for &quot;off&quot;) Allowed values: reboot, off, poweroff</longdesc>
- <shortdesc lang="en">Action to send to fence device when a node needs to be fenced (&quot;poweroff&quot; is a deprecated alias for &quot;off&quot;)</shortdesc>
+ <longdesc lang="en">
+ Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off") Allowed values: reboot, off, poweroff
+ </longdesc>
+ <shortdesc lang="en">
+ Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")
+ </shortdesc>
<content type="select" default="">
- <option value="reboot" />
- <option value="off" />
- <option value="poweroff" />
+ <option value="reboot"/>
+ <option value="off"/>
+ <option value="poweroff"/>
</content>
</parameter>
<parameter name="stonith-timeout">
- <longdesc lang="en">This value is not used by Pacemaker, but is kept for backward compatibility, and certain legacy fence agents might use it.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only *** Unused by Pacemaker</shortdesc>
+ <longdesc lang="en">
+ How long to wait for on, off, and reboot fence actions to complete by default
+ </longdesc>
+ <shortdesc lang="en">
+ How long to wait for on, off, and reboot fence actions to complete by default
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="have-watchdog">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">Whether watchdog integration is enabled</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Whether watchdog integration is enabled
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="concurrent-fencing">
- <longdesc lang="en">Allow performing fencing operations in parallel</longdesc>
- <shortdesc lang="en">Allow performing fencing operations in parallel</shortdesc>
+ <longdesc lang="en">
+ Allow performing fencing operations in parallel
+ </longdesc>
+ <shortdesc lang="en">
+ Allow performing fencing operations in parallel
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="startup-fencing">
- <longdesc lang="en">Setting this to false may lead to a &quot;split-brain&quot; situation,potentially leading to data loss and/or service unavailability.</longdesc>
- <shortdesc lang="en">*** Advanced Use Only *** Whether to fence unseen nodes at start-up</shortdesc>
+ <longdesc lang="en">
+ Setting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Whether to fence unseen nodes at start-up
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="priority-fencing-delay">
- <longdesc lang="en">Apply specified delay for the fencings that are targeting the lost nodes with the highest total resource priority in case we don&apos;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.</longdesc>
- <shortdesc lang="en">Apply fencing delay targeting the lost nodes with the highest total resource priority</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Apply fencing delay targeting the lost nodes with the highest total resource priority
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="node-pending-timeout">
- <longdesc lang="en">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.</longdesc>
- <shortdesc lang="en">How long to wait for a node that has joined the cluster to join the controller process group</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ How long to wait for a node that has joined the cluster to join the controller process group
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="cluster-delay">
- <longdesc lang="en">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&apos;s own timeout). The &quot;correct&quot; value will depend on the speed and load of your network and cluster nodes.</longdesc>
- <shortdesc lang="en">Maximum time for node-to-node communication</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Maximum time for node-to-node communication
+ </shortdesc>
<content type="time" default=""/>
</parameter>
<parameter name="batch-limit">
- <longdesc lang="en">The &quot;correct&quot; 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.</longdesc>
- <shortdesc lang="en">Maximum number of jobs that the cluster may execute in parallel across all nodes</shortdesc>
+ <longdesc lang="en">
+ 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.
+ </longdesc>
+ <shortdesc lang="en">
+ Maximum number of jobs that the cluster may execute in parallel across all nodes
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="migration-limit">
- <longdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</longdesc>
- <shortdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</shortdesc>
+ <longdesc lang="en">
+ The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)
+ </longdesc>
+ <shortdesc lang="en">
+ The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="stop-all-resources">
- <longdesc lang="en">Whether the cluster should stop all active resources</longdesc>
- <shortdesc lang="en">Whether the cluster should stop all active resources</shortdesc>
+ <longdesc lang="en">
+ Whether the cluster should stop all active resources
+ </longdesc>
+ <shortdesc lang="en">
+ Whether the cluster should stop all active resources
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="stop-orphan-resources">
- <longdesc lang="en">Whether to stop resources that were removed from the configuration</longdesc>
- <shortdesc lang="en">Whether to stop resources that were removed from the configuration</shortdesc>
+ <longdesc lang="en">
+ Whether to stop resources that were removed from the configuration
+ </longdesc>
+ <shortdesc lang="en">
+ Whether to stop resources that were removed from the configuration
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="stop-orphan-actions">
- <longdesc lang="en">Whether to cancel recurring actions removed from the configuration</longdesc>
- <shortdesc lang="en">Whether to cancel recurring actions removed from the configuration</shortdesc>
+ <longdesc lang="en">
+ Whether to cancel recurring actions removed from the configuration
+ </longdesc>
+ <shortdesc lang="en">
+ Whether to cancel recurring actions removed from the configuration
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="remove-after-stop">
- <longdesc lang="en">Values other than default are poorly tested and potentially dangerous. This option will be removed in a future release.</longdesc>
- <shortdesc lang="en">*** Deprecated *** Whether to remove stopped resources from the executor</shortdesc>
+ <longdesc lang="en">
+ Values other than default are poorly tested and potentially dangerous.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Deprecated *** Whether to remove stopped resources from the executor
+ </shortdesc>
<content type="boolean" default=""/>
</parameter>
<parameter name="pe-error-series-max">
- <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
- <shortdesc lang="en">The number of scheduler inputs resulting in errors to save</shortdesc>
+ <longdesc lang="en">
+ Zero to disable, -1 to store unlimited.
+ </longdesc>
+ <shortdesc lang="en">
+ The number of scheduler inputs resulting in errors to save
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pe-warn-series-max">
- <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
- <shortdesc lang="en">The number of scheduler inputs resulting in warnings to save</shortdesc>
+ <longdesc lang="en">
+ Zero to disable, -1 to store unlimited.
+ </longdesc>
+ <shortdesc lang="en">
+ The number of scheduler inputs resulting in warnings to save
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="pe-input-series-max">
- <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
- <shortdesc lang="en">The number of scheduler inputs without errors or warnings to save</shortdesc>
+ <longdesc lang="en">
+ Zero to disable, -1 to store unlimited.
+ </longdesc>
+ <shortdesc lang="en">
+ The number of scheduler inputs without errors or warnings to save
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="node-health-strategy">
- <longdesc lang="en">Requires external entities to create node attributes (named with the prefix &quot;#health&quot;) with values &quot;red&quot;, &quot;yellow&quot;, or &quot;green&quot;. Allowed values: none, migrate-on-red, only-green, progressive, custom</longdesc>
- <shortdesc lang="en">How cluster should react to node health attributes</shortdesc>
+ <longdesc lang="en">
+ Requires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green". Allowed values: none, migrate-on-red, only-green, progressive, custom
+ </longdesc>
+ <shortdesc lang="en">
+ How cluster should react to node health attributes
+ </shortdesc>
<content type="select" default="">
- <option value="none" />
- <option value="migrate-on-red" />
- <option value="only-green" />
- <option value="progressive" />
- <option value="custom" />
+ <option value="none"/>
+ <option value="migrate-on-red"/>
+ <option value="only-green"/>
+ <option value="progressive"/>
+ <option value="custom"/>
</content>
</parameter>
<parameter name="node-health-base">
- <longdesc lang="en">Only used when &quot;node-health-strategy&quot; is set to &quot;progressive&quot;.</longdesc>
- <shortdesc lang="en">Base health score assigned to a node</shortdesc>
+ <longdesc lang="en">
+ Only used when "node-health-strategy" is set to "progressive".
+ </longdesc>
+ <shortdesc lang="en">
+ Base health score assigned to a node
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="node-health-green">
- <longdesc lang="en">Only used when &quot;node-health-strategy&quot; is set to &quot;custom&quot; or &quot;progressive&quot;.</longdesc>
- <shortdesc lang="en">The score to use for a node health attribute whose value is &quot;green&quot;</shortdesc>
+ <longdesc lang="en">
+ Only used when "node-health-strategy" is set to "custom" or "progressive".
+ </longdesc>
+ <shortdesc lang="en">
+ The score to use for a node health attribute whose value is "green"
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="node-health-yellow">
- <longdesc lang="en">Only used when &quot;node-health-strategy&quot; is set to &quot;custom&quot; or &quot;progressive&quot;.</longdesc>
- <shortdesc lang="en">The score to use for a node health attribute whose value is &quot;yellow&quot;</shortdesc>
+ <longdesc lang="en">
+ Only used when "node-health-strategy" is set to "custom" or "progressive".
+ </longdesc>
+ <shortdesc lang="en">
+ The score to use for a node health attribute whose value is "yellow"
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="node-health-red">
- <longdesc lang="en">Only used when &quot;node-health-strategy&quot; is set to &quot;custom&quot; or &quot;progressive&quot;.</longdesc>
- <shortdesc lang="en">The score to use for a node health attribute whose value is &quot;red&quot;</shortdesc>
+ <longdesc lang="en">
+ Only used when "node-health-strategy" is set to "custom" or "progressive".
+ </longdesc>
+ <shortdesc lang="en">
+ The score to use for a node health attribute whose value is "red"
+ </shortdesc>
<content type="integer" default=""/>
</parameter>
<parameter name="placement-strategy">
- <longdesc lang="en">How the cluster should allocate resources to nodes Allowed values: default, utilization, minimal, balanced</longdesc>
- <shortdesc lang="en">How the cluster should allocate resources to nodes</shortdesc>
+ <longdesc lang="en">
+ How the cluster should allocate resources to nodes Allowed values: default, utilization, minimal, balanced
+ </longdesc>
+ <shortdesc lang="en">
+ How the cluster should allocate resources to nodes
+ </shortdesc>
<content type="select" default="">
- <option value="default" />
- <option value="utilization" />
- <option value="minimal" />
- <option value="balanced" />
+ <option value="default"/>
+ <option value="utilization"/>
+ <option value="minimal"/>
+ <option value="balanced"/>
</content>
</parameter>
</parameters>
diff --git a/cts/cli/regression.rules.exp b/cts/cli/regression.rules.exp
index cdfb5d1..a439773 100644
--- a/cts/cli/regression.rules.exp
+++ b/cts/cli/regression.rules.exp
@@ -37,7 +37,6 @@ log_xmllib_err error: XML Error: Entity: line 1: parser error : Start tag expec
log_xmllib_err error: XML Error: invalidxml
log_xmllib_err error: XML Error: ^
crm_rule: Couldn't parse input string: invalidxml
-
=#=#=#= End test: crm_rule given invalid input XML - Invalid data given (65) =#=#=#=
* Passed: crm_rule - crm_rule given invalid input XML
=#=#=#= Begin test: crm_rule given invalid input XML (XML) =#=#=#=
@@ -50,8 +49,7 @@ log_xmllib_err error: XML Error: ^
<pacemaker-result api-version="X" request="crm_rule -c -r blahblah -X invalidxml --output-as=xml">
<status code="65" message="Invalid data given">
<errors>
- <error>crm_rule: Couldn't parse input string: invalidxml
-</error>
+ <error>crm_rule: Couldn't parse input string: invalidxml</error>
</errors>
</status>
</pacemaker-result>
@@ -65,7 +63,6 @@ log_xmllib_err error: XML Error: Entity: line 1: parser error : Start tag expec
log_xmllib_err error: XML Error: invalidxml
log_xmllib_err error: XML Error: ^
crm_rule: Couldn't parse input from STDIN
-
=#=#=#= End test: crm_rule given invalid input XML on stdin - Invalid data given (65) =#=#=#=
* Passed: echo - crm_rule given invalid input XML on stdin
=#=#=#= Begin test: crm_rule given invalid input XML on stdin (XML) =#=#=#=
@@ -78,8 +75,7 @@ log_xmllib_err error: XML Error: ^
<pacemaker-result api-version="X" request="crm_rule -c -r blahblah -X - --output-as=xml">
<status code="65" message="Invalid data given">
<errors>
- <error>crm_rule: Couldn't parse input from STDIN
-</error>
+ <error>crm_rule: Couldn't parse input from STDIN</error>
</errors>
</status>
</pacemaker-result>
diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
index accf781..6eef178 100644
--- a/cts/cli/regression.tools.exp
+++ b/cts/cli/regression.tools.exp
@@ -23,6 +23,910 @@ A new shadow instance was created. To begin using it, enter the following into y
</cib>
=#=#=#= End test: Validate CIB - OK (0) =#=#=#=
* Passed: cibadmin - Validate CIB
+=#=#=#= Begin test: List all available options (invalid type) =#=#=#=
+crm_attribute: Invalid --list-options value 'asdf'. Allowed values: cluster
+=#=#=#= End test: List all available options (invalid type) - Incorrect usage (64) =#=#=#=
+* Passed: crm_attribute - List all available options (invalid type)
+=#=#=#= Begin test: List all available options (invalid type) (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute --list-options=asdf --output-as=xml">
+ <status code="64" message="Incorrect usage">
+ <errors>
+ <error>crm_attribute: Invalid --list-options value 'asdf'. Allowed values: cluster</error>
+ </errors>
+ </status>
+</pacemaker-result>
+=#=#=#= End test: List all available options (invalid type) (XML) - Incorrect usage (64) =#=#=#=
+* Passed: crm_attribute - List all available options (invalid type) (XML)
+=#=#=#= Begin test: List non-advanced cluster options =#=#=#=
+Pacemaker cluster options
+
+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.
+
+ * dc-version: Pacemaker version on cluster node elected Designated Controller (DC)
+ * Includes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.
+ * Possible values (generated by Pacemaker): version (no default)
+
+ * cluster-infrastructure: The messaging layer on which Pacemaker is currently running
+ * Used for informational and diagnostic purposes.
+ * Possible values (generated by Pacemaker): string (no default)
+
+ * cluster-name: An arbitrary name for the cluster
+ * 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.
+ * Possible values: string (no default)
+
+ * dc-deadtime: How long to wait for a response from other nodes during start-up
+ * The optimal value will depend on the speed and load of your network and the type of switches used.
+ * Possible values: duration (default: )
+
+ * cluster-recheck-interval: Polling interval to recheck cluster state and evaluate rules with date specifications
+ * 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").
+ * Possible values: duration (default: )
+
+ * fence-reaction: How a cluster node should react if notified of its own fencing
+ * 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.
+ * Possible values: "stop" (default), "panic"
+
+ * no-quorum-policy: What to do when the cluster does not have quorum
+ * Possible values: "stop" (default), "freeze", "ignore", "demote", "suicide"
+
+ * shutdown-lock: Whether to lock resources to a cleanly shut down node
+ * 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.
+ * Possible values: boolean (default: )
+
+ * shutdown-lock-limit: Do not lock resources to a cleanly shut down node longer than this
+ * 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.
+ * Possible values: duration (default: )
+
+ * enable-acl: Enable Access Control Lists (ACLs) for the CIB
+ * Possible values: boolean (default: )
+
+ * symmetric-cluster: Whether resources can run on any node by default
+ * Possible values: boolean (default: )
+
+ * maintenance-mode: Whether the cluster should refrain from monitoring, starting, and stopping resources
+ * Possible values: boolean (default: )
+
+ * start-failure-is-fatal: Whether a start failure should prevent a resource from being recovered on the same node
+ * 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.
+ * Possible values: boolean (default: )
+
+ * enable-startup-probes: Whether the cluster should check for active resources during start-up
+ * Possible values: boolean (default: )
+
+ * stonith-action: Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")
+ * Possible values: "reboot" (default), "off", "poweroff"
+
+ * stonith-timeout: How long to wait for on, off, and reboot fence actions to complete by default
+ * Possible values: duration (default: )
+
+ * have-watchdog: Whether watchdog integration is enabled
+ * 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.
+ * Possible values (generated by Pacemaker): boolean (default: )
+
+ * stonith-watchdog-timeout: How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use
+ * 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.
+ * Possible values: timeout (default: )
+
+ * stonith-max-attempts: How many times fencing can fail before it will no longer be immediately re-attempted on a target
+ * Possible values: score (default: )
+
+ * concurrent-fencing: Allow performing fencing operations in parallel
+ * Possible values: boolean (default: )
+
+ * priority-fencing-delay: Apply fencing delay targeting the lost nodes with the highest total resource priority
+ * 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.
+ * Possible values: duration (default: )
+
+ * node-pending-timeout: How long to wait for a node that has joined the cluster to join the controller process group
+ * 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.
+ * Possible values: duration (default: )
+
+ * cluster-delay: Maximum time for node-to-node communication
+ * 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.
+ * Possible values: duration (default: )
+
+ * load-threshold: Maximum amount of system load that should be used by cluster nodes
+ * The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit
+ * Possible values: percentage (default: )
+
+ * node-action-limit: Maximum number of jobs that can be scheduled per node (defaults to 2x cores)
+ * Possible values: integer (default: )
+
+ * batch-limit: Maximum number of jobs that the cluster may execute in parallel across all nodes
+ * 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.
+ * Possible values: integer (default: )
+
+ * migration-limit: The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)
+ * Possible values: integer (default: )
+
+ * cluster-ipc-limit: Maximum IPC message backlog before disconnecting a cluster daemon
+ * 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).
+ * Possible values: nonnegative_integer (default: )
+
+ * stop-all-resources: Whether the cluster should stop all active resources
+ * Possible values: boolean (default: )
+
+ * stop-orphan-resources: Whether to stop resources that were removed from the configuration
+ * Possible values: boolean (default: )
+
+ * stop-orphan-actions: Whether to cancel recurring actions removed from the configuration
+ * Possible values: boolean (default: )
+
+ * pe-error-series-max: The number of scheduler inputs resulting in errors to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * pe-warn-series-max: The number of scheduler inputs resulting in warnings to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * pe-input-series-max: The number of scheduler inputs without errors or warnings to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * node-health-strategy: How cluster should react to node health attributes
+ * Requires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".
+ * Possible values: "none" (default), "migrate-on-red", "only-green", "progressive", "custom"
+
+ * node-health-base: Base health score assigned to a node
+ * Only used when "node-health-strategy" is set to "progressive".
+ * Possible values: score (default: )
+
+ * node-health-green: The score to use for a node health attribute whose value is "green"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * node-health-yellow: The score to use for a node health attribute whose value is "yellow"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * node-health-red: The score to use for a node health attribute whose value is "red"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * placement-strategy: How the cluster should allocate resources to nodes
+ * Possible values: "default" (default), "utilization", "minimal", "balanced"
+=#=#=#= End test: List non-advanced cluster options - OK (0) =#=#=#=
+* Passed: crm_attribute - List non-advanced cluster options
+=#=#=#= Begin test: List non-advanced cluster options (XML) (shows all) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute --list-options=cluster --output-as=xml">
+ <resource-agent name="cluster-options" version="">
+ <version>1.1</version>
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Pacemaker cluster options</shortdesc>
+ <parameters>
+ <parameter name="dc-version" advanced="0" generated="1">
+ <longdesc lang="en">Includes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.</longdesc>
+ <shortdesc lang="en">Pacemaker version on cluster node elected Designated Controller (DC)</shortdesc>
+ <content type="version"/>
+ </parameter>
+ <parameter name="cluster-infrastructure" advanced="0" generated="1">
+ <longdesc lang="en">Used for informational and diagnostic purposes.</longdesc>
+ <shortdesc lang="en">The messaging layer on which Pacemaker is currently running</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="cluster-name" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An arbitrary name for the cluster</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="dc-deadtime" advanced="0" generated="0">
+ <longdesc lang="en">The optimal value will depend on the speed and load of your network and the type of switches used.</longdesc>
+ <shortdesc lang="en">How long to wait for a response from other nodes during start-up</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="cluster-recheck-interval" advanced="0" generated="0">
+ <longdesc lang="en">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").</longdesc>
+ <shortdesc lang="en">Polling interval to recheck cluster state and evaluate rules with date specifications</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="fence-reaction" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How a cluster node should react if notified of its own fencing</shortdesc>
+ <content type="select" default="">
+ <option value="stop"/>
+ <option value="panic"/>
+ </content>
+ </parameter>
+ <parameter name="election-timeout" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">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.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="shutdown-escalation" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">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.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="join-integration-timeout" advanced="1" generated="0">
+ <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
+ <shortdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="join-finalization-timeout" advanced="1" generated="0">
+ <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
+ <shortdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="transition-delay" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enabling this option will slow down cluster recovery under all conditions</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="no-quorum-policy" advanced="0" generated="0">
+ <longdesc lang="en">What to do when the cluster does not have quorum</longdesc>
+ <shortdesc lang="en">What to do when the cluster does not have quorum</shortdesc>
+ <content type="select" default="">
+ <option value="stop"/>
+ <option value="freeze"/>
+ <option value="ignore"/>
+ <option value="demote"/>
+ <option value="suicide"/>
+ </content>
+ </parameter>
+ <parameter name="shutdown-lock" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether to lock resources to a cleanly shut down node</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="shutdown-lock-limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Do not lock resources to a cleanly shut down node longer than this</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="enable-acl" advanced="0" generated="0">
+ <longdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</longdesc>
+ <shortdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="symmetric-cluster" advanced="0" generated="0">
+ <longdesc lang="en">Whether resources can run on any node by default</longdesc>
+ <shortdesc lang="en">Whether resources can run on any node by default</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="maintenance-mode" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</longdesc>
+ <shortdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="start-failure-is-fatal" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether a start failure should prevent a resource from being recovered on the same node</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="enable-startup-probes" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should check for active resources during start-up</longdesc>
+ <shortdesc lang="en">Whether the cluster should check for active resources during start-up</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-enabled" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether nodes may be fenced as part of recovery</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-action" advanced="0" generated="0">
+ <longdesc lang="en">Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")</longdesc>
+ <shortdesc lang="en">Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")</shortdesc>
+ <content type="select" default="">
+ <option value="reboot"/>
+ <option value="off"/>
+ <option value="poweroff"/>
+ </content>
+ </parameter>
+ <parameter name="stonith-timeout" advanced="0" generated="0">
+ <longdesc lang="en">How long to wait for on, off, and reboot fence actions to complete by default</longdesc>
+ <shortdesc lang="en">How long to wait for on, off, and reboot fence actions to complete by default</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="have-watchdog" advanced="0" generated="1">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether watchdog integration is enabled</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-watchdog-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="stonith-max-attempts" advanced="0" generated="0">
+ <longdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</longdesc>
+ <shortdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="concurrent-fencing" advanced="0" generated="0">
+ <longdesc lang="en">Allow performing fencing operations in parallel</longdesc>
+ <shortdesc lang="en">Allow performing fencing operations in parallel</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="startup-fencing" advanced="1" generated="0">
+ <longdesc lang="en">Setting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.</longdesc>
+ <shortdesc lang="en">Whether to fence unseen nodes at start-up</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="priority-fencing-delay" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Apply fencing delay targeting the lost nodes with the highest total resource priority</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="node-pending-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How long to wait for a node that has joined the cluster to join the controller process group</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="cluster-delay" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Maximum time for node-to-node communication</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="load-threshold" advanced="0" generated="0">
+ <longdesc lang="en">The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit</longdesc>
+ <shortdesc lang="en">Maximum amount of system load that should be used by cluster nodes</shortdesc>
+ <content type="percentage" default=""/>
+ </parameter>
+ <parameter name="node-action-limit" advanced="0" generated="0">
+ <longdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</longdesc>
+ <shortdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="batch-limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Maximum number of jobs that the cluster may execute in parallel across all nodes</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="migration-limit" advanced="0" generated="0">
+ <longdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</longdesc>
+ <shortdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="cluster-ipc-limit" advanced="0" generated="0">
+ <longdesc lang="en">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).</longdesc>
+ <shortdesc lang="en">Maximum IPC message backlog before disconnecting a cluster daemon</shortdesc>
+ <content type="nonnegative_integer" default=""/>
+ </parameter>
+ <parameter name="stop-all-resources" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should stop all active resources</longdesc>
+ <shortdesc lang="en">Whether the cluster should stop all active resources</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stop-orphan-resources" advanced="0" generated="0">
+ <longdesc lang="en">Whether to stop resources that were removed from the configuration</longdesc>
+ <shortdesc lang="en">Whether to stop resources that were removed from the configuration</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stop-orphan-actions" advanced="0" generated="0">
+ <longdesc lang="en">Whether to cancel recurring actions removed from the configuration</longdesc>
+ <shortdesc lang="en">Whether to cancel recurring actions removed from the configuration</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="remove-after-stop" advanced="0" generated="0">
+ <deprecated/>
+ <longdesc lang="en">Values other than default are poorly tested and potentially dangerous.</longdesc>
+ <shortdesc lang="en">Whether to remove stopped resources from the executor</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="pe-error-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs resulting in errors to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pe-warn-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs resulting in warnings to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pe-input-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs without errors or warnings to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="node-health-strategy" advanced="0" generated="0">
+ <longdesc lang="en">Requires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".</longdesc>
+ <shortdesc lang="en">How cluster should react to node health attributes</shortdesc>
+ <content type="select" default="">
+ <option value="none"/>
+ <option value="migrate-on-red"/>
+ <option value="only-green"/>
+ <option value="progressive"/>
+ <option value="custom"/>
+ </content>
+ </parameter>
+ <parameter name="node-health-base" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "progressive".</longdesc>
+ <shortdesc lang="en">Base health score assigned to a node</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-green" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "green"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-yellow" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "yellow"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-red" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "red"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="placement-strategy" advanced="0" generated="0">
+ <longdesc lang="en">How the cluster should allocate resources to nodes</longdesc>
+ <shortdesc lang="en">How the cluster should allocate resources to nodes</shortdesc>
+ <content type="select" default="">
+ <option value="default"/>
+ <option value="utilization"/>
+ <option value="minimal"/>
+ <option value="balanced"/>
+ </content>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List non-advanced cluster options (XML) (shows all) - OK (0) =#=#=#=
+* Passed: crm_attribute - List non-advanced cluster options (XML) (shows all)
+=#=#=#= Begin test: List all available cluster options =#=#=#=
+Pacemaker cluster options
+
+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.
+
+ * dc-version: Pacemaker version on cluster node elected Designated Controller (DC)
+ * Includes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.
+ * Possible values (generated by Pacemaker): version (no default)
+
+ * cluster-infrastructure: The messaging layer on which Pacemaker is currently running
+ * Used for informational and diagnostic purposes.
+ * Possible values (generated by Pacemaker): string (no default)
+
+ * cluster-name: An arbitrary name for the cluster
+ * 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.
+ * Possible values: string (no default)
+
+ * dc-deadtime: How long to wait for a response from other nodes during start-up
+ * The optimal value will depend on the speed and load of your network and the type of switches used.
+ * Possible values: duration (default: )
+
+ * cluster-recheck-interval: Polling interval to recheck cluster state and evaluate rules with date specifications
+ * 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").
+ * Possible values: duration (default: )
+
+ * fence-reaction: How a cluster node should react if notified of its own fencing
+ * 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.
+ * Possible values: "stop" (default), "panic"
+
+ * no-quorum-policy: What to do when the cluster does not have quorum
+ * Possible values: "stop" (default), "freeze", "ignore", "demote", "suicide"
+
+ * shutdown-lock: Whether to lock resources to a cleanly shut down node
+ * 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.
+ * Possible values: boolean (default: )
+
+ * shutdown-lock-limit: Do not lock resources to a cleanly shut down node longer than this
+ * 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.
+ * Possible values: duration (default: )
+
+ * enable-acl: Enable Access Control Lists (ACLs) for the CIB
+ * Possible values: boolean (default: )
+
+ * symmetric-cluster: Whether resources can run on any node by default
+ * Possible values: boolean (default: )
+
+ * maintenance-mode: Whether the cluster should refrain from monitoring, starting, and stopping resources
+ * Possible values: boolean (default: )
+
+ * start-failure-is-fatal: Whether a start failure should prevent a resource from being recovered on the same node
+ * 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.
+ * Possible values: boolean (default: )
+
+ * enable-startup-probes: Whether the cluster should check for active resources during start-up
+ * Possible values: boolean (default: )
+
+ * stonith-action: Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")
+ * Possible values: "reboot" (default), "off", "poweroff"
+
+ * stonith-timeout: How long to wait for on, off, and reboot fence actions to complete by default
+ * Possible values: duration (default: )
+
+ * have-watchdog: Whether watchdog integration is enabled
+ * 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.
+ * Possible values (generated by Pacemaker): boolean (default: )
+
+ * stonith-watchdog-timeout: How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use
+ * 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.
+ * Possible values: timeout (default: )
+
+ * stonith-max-attempts: How many times fencing can fail before it will no longer be immediately re-attempted on a target
+ * Possible values: score (default: )
+
+ * concurrent-fencing: Allow performing fencing operations in parallel
+ * Possible values: boolean (default: )
+
+ * priority-fencing-delay: Apply fencing delay targeting the lost nodes with the highest total resource priority
+ * 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.
+ * Possible values: duration (default: )
+
+ * node-pending-timeout: How long to wait for a node that has joined the cluster to join the controller process group
+ * 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.
+ * Possible values: duration (default: )
+
+ * cluster-delay: Maximum time for node-to-node communication
+ * 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.
+ * Possible values: duration (default: )
+
+ * load-threshold: Maximum amount of system load that should be used by cluster nodes
+ * The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit
+ * Possible values: percentage (default: )
+
+ * node-action-limit: Maximum number of jobs that can be scheduled per node (defaults to 2x cores)
+ * Possible values: integer (default: )
+
+ * batch-limit: Maximum number of jobs that the cluster may execute in parallel across all nodes
+ * 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.
+ * Possible values: integer (default: )
+
+ * migration-limit: The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)
+ * Possible values: integer (default: )
+
+ * cluster-ipc-limit: Maximum IPC message backlog before disconnecting a cluster daemon
+ * 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).
+ * Possible values: nonnegative_integer (default: )
+
+ * stop-all-resources: Whether the cluster should stop all active resources
+ * Possible values: boolean (default: )
+
+ * stop-orphan-resources: Whether to stop resources that were removed from the configuration
+ * Possible values: boolean (default: )
+
+ * stop-orphan-actions: Whether to cancel recurring actions removed from the configuration
+ * Possible values: boolean (default: )
+
+ * pe-error-series-max: The number of scheduler inputs resulting in errors to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * pe-warn-series-max: The number of scheduler inputs resulting in warnings to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * pe-input-series-max: The number of scheduler inputs without errors or warnings to save
+ * Zero to disable, -1 to store unlimited.
+ * Possible values: integer (default: )
+
+ * node-health-strategy: How cluster should react to node health attributes
+ * Requires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".
+ * Possible values: "none" (default), "migrate-on-red", "only-green", "progressive", "custom"
+
+ * node-health-base: Base health score assigned to a node
+ * Only used when "node-health-strategy" is set to "progressive".
+ * Possible values: score (default: )
+
+ * node-health-green: The score to use for a node health attribute whose value is "green"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * node-health-yellow: The score to use for a node health attribute whose value is "yellow"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * node-health-red: The score to use for a node health attribute whose value is "red"
+ * Only used when "node-health-strategy" is set to "custom" or "progressive".
+ * Possible values: score (default: )
+
+ * placement-strategy: How the cluster should allocate resources to nodes
+ * Possible values: "default" (default), "utilization", "minimal", "balanced"
+
+ * ADVANCED OPTIONS:
+
+ * election-timeout: 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.
+ * Possible values: duration (default: )
+
+ * shutdown-escalation: 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.
+ * Possible values: duration (default: )
+
+ * join-integration-timeout: If you need to adjust this value, it probably indicates the presence of a bug.
+ * Possible values: duration (default: )
+
+ * join-finalization-timeout: If you need to adjust this value, it probably indicates the presence of a bug.
+ * Possible values: duration (default: )
+
+ * transition-delay: Enabling this option will slow down cluster recovery under all conditions
+ * 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.
+ * Possible values: duration (default: )
+
+ * stonith-enabled: Whether nodes may be fenced as part of recovery
+ * 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.
+ * Possible values: boolean (default: )
+
+ * startup-fencing: Whether to fence unseen nodes at start-up
+ * Setting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.
+ * Possible values: boolean (default: )
+
+ * DEPRECATED OPTIONS (will be removed in a future release):
+
+ * remove-after-stop: Whether to remove stopped resources from the executor
+ * Values other than default are poorly tested and potentially dangerous.
+ * Possible values: boolean (default: )
+=#=#=#= End test: List all available cluster options - OK (0) =#=#=#=
+* Passed: crm_attribute - List all available cluster options
+=#=#=#= Begin test: List all available cluster options (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute --list-options=cluster --all --output-as=xml">
+ <resource-agent name="cluster-options" version="">
+ <version>1.1</version>
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Pacemaker cluster options</shortdesc>
+ <parameters>
+ <parameter name="dc-version" advanced="0" generated="1">
+ <longdesc lang="en">Includes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.</longdesc>
+ <shortdesc lang="en">Pacemaker version on cluster node elected Designated Controller (DC)</shortdesc>
+ <content type="version"/>
+ </parameter>
+ <parameter name="cluster-infrastructure" advanced="0" generated="1">
+ <longdesc lang="en">Used for informational and diagnostic purposes.</longdesc>
+ <shortdesc lang="en">The messaging layer on which Pacemaker is currently running</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="cluster-name" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An arbitrary name for the cluster</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="dc-deadtime" advanced="0" generated="0">
+ <longdesc lang="en">The optimal value will depend on the speed and load of your network and the type of switches used.</longdesc>
+ <shortdesc lang="en">How long to wait for a response from other nodes during start-up</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="cluster-recheck-interval" advanced="0" generated="0">
+ <longdesc lang="en">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").</longdesc>
+ <shortdesc lang="en">Polling interval to recheck cluster state and evaluate rules with date specifications</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="fence-reaction" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How a cluster node should react if notified of its own fencing</shortdesc>
+ <content type="select" default="">
+ <option value="stop"/>
+ <option value="panic"/>
+ </content>
+ </parameter>
+ <parameter name="election-timeout" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">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.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="shutdown-escalation" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">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.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="join-integration-timeout" advanced="1" generated="0">
+ <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
+ <shortdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="join-finalization-timeout" advanced="1" generated="0">
+ <longdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</longdesc>
+ <shortdesc lang="en">If you need to adjust this value, it probably indicates the presence of a bug.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="transition-delay" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enabling this option will slow down cluster recovery under all conditions</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="no-quorum-policy" advanced="0" generated="0">
+ <longdesc lang="en">What to do when the cluster does not have quorum</longdesc>
+ <shortdesc lang="en">What to do when the cluster does not have quorum</shortdesc>
+ <content type="select" default="">
+ <option value="stop"/>
+ <option value="freeze"/>
+ <option value="ignore"/>
+ <option value="demote"/>
+ <option value="suicide"/>
+ </content>
+ </parameter>
+ <parameter name="shutdown-lock" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether to lock resources to a cleanly shut down node</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="shutdown-lock-limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Do not lock resources to a cleanly shut down node longer than this</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="enable-acl" advanced="0" generated="0">
+ <longdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</longdesc>
+ <shortdesc lang="en">Enable Access Control Lists (ACLs) for the CIB</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="symmetric-cluster" advanced="0" generated="0">
+ <longdesc lang="en">Whether resources can run on any node by default</longdesc>
+ <shortdesc lang="en">Whether resources can run on any node by default</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="maintenance-mode" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</longdesc>
+ <shortdesc lang="en">Whether the cluster should refrain from monitoring, starting, and stopping resources</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="start-failure-is-fatal" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether a start failure should prevent a resource from being recovered on the same node</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="enable-startup-probes" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should check for active resources during start-up</longdesc>
+ <shortdesc lang="en">Whether the cluster should check for active resources during start-up</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-enabled" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether nodes may be fenced as part of recovery</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-action" advanced="0" generated="0">
+ <longdesc lang="en">Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")</longdesc>
+ <shortdesc lang="en">Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")</shortdesc>
+ <content type="select" default="">
+ <option value="reboot"/>
+ <option value="off"/>
+ <option value="poweroff"/>
+ </content>
+ </parameter>
+ <parameter name="stonith-timeout" advanced="0" generated="0">
+ <longdesc lang="en">How long to wait for on, off, and reboot fence actions to complete by default</longdesc>
+ <shortdesc lang="en">How long to wait for on, off, and reboot fence actions to complete by default</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="have-watchdog" advanced="0" generated="1">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether watchdog integration is enabled</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stonith-watchdog-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in use</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="stonith-max-attempts" advanced="0" generated="0">
+ <longdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</longdesc>
+ <shortdesc lang="en">How many times fencing can fail before it will no longer be immediately re-attempted on a target</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="concurrent-fencing" advanced="0" generated="0">
+ <longdesc lang="en">Allow performing fencing operations in parallel</longdesc>
+ <shortdesc lang="en">Allow performing fencing operations in parallel</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="startup-fencing" advanced="1" generated="0">
+ <longdesc lang="en">Setting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.</longdesc>
+ <shortdesc lang="en">Whether to fence unseen nodes at start-up</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="priority-fencing-delay" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Apply fencing delay targeting the lost nodes with the highest total resource priority</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="node-pending-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">How long to wait for a node that has joined the cluster to join the controller process group</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="cluster-delay" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Maximum time for node-to-node communication</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="load-threshold" advanced="0" generated="0">
+ <longdesc lang="en">The cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limit</longdesc>
+ <shortdesc lang="en">Maximum amount of system load that should be used by cluster nodes</shortdesc>
+ <content type="percentage" default=""/>
+ </parameter>
+ <parameter name="node-action-limit" advanced="0" generated="0">
+ <longdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</longdesc>
+ <shortdesc lang="en">Maximum number of jobs that can be scheduled per node (defaults to 2x cores)</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="batch-limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Maximum number of jobs that the cluster may execute in parallel across all nodes</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="migration-limit" advanced="0" generated="0">
+ <longdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</longdesc>
+ <shortdesc lang="en">The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="cluster-ipc-limit" advanced="0" generated="0">
+ <longdesc lang="en">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).</longdesc>
+ <shortdesc lang="en">Maximum IPC message backlog before disconnecting a cluster daemon</shortdesc>
+ <content type="nonnegative_integer" default=""/>
+ </parameter>
+ <parameter name="stop-all-resources" advanced="0" generated="0">
+ <longdesc lang="en">Whether the cluster should stop all active resources</longdesc>
+ <shortdesc lang="en">Whether the cluster should stop all active resources</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stop-orphan-resources" advanced="0" generated="0">
+ <longdesc lang="en">Whether to stop resources that were removed from the configuration</longdesc>
+ <shortdesc lang="en">Whether to stop resources that were removed from the configuration</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="stop-orphan-actions" advanced="0" generated="0">
+ <longdesc lang="en">Whether to cancel recurring actions removed from the configuration</longdesc>
+ <shortdesc lang="en">Whether to cancel recurring actions removed from the configuration</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="remove-after-stop" advanced="0" generated="0">
+ <deprecated/>
+ <longdesc lang="en">Values other than default are poorly tested and potentially dangerous.</longdesc>
+ <shortdesc lang="en">Whether to remove stopped resources from the executor</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="pe-error-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs resulting in errors to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pe-warn-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs resulting in warnings to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pe-input-series-max" advanced="0" generated="0">
+ <longdesc lang="en">Zero to disable, -1 to store unlimited.</longdesc>
+ <shortdesc lang="en">The number of scheduler inputs without errors or warnings to save</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="node-health-strategy" advanced="0" generated="0">
+ <longdesc lang="en">Requires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".</longdesc>
+ <shortdesc lang="en">How cluster should react to node health attributes</shortdesc>
+ <content type="select" default="">
+ <option value="none"/>
+ <option value="migrate-on-red"/>
+ <option value="only-green"/>
+ <option value="progressive"/>
+ <option value="custom"/>
+ </content>
+ </parameter>
+ <parameter name="node-health-base" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "progressive".</longdesc>
+ <shortdesc lang="en">Base health score assigned to a node</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-green" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "green"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-yellow" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "yellow"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="node-health-red" advanced="0" generated="0">
+ <longdesc lang="en">Only used when "node-health-strategy" is set to "custom" or "progressive".</longdesc>
+ <shortdesc lang="en">The score to use for a node health attribute whose value is "red"</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="placement-strategy" advanced="0" generated="0">
+ <longdesc lang="en">How the cluster should allocate resources to nodes</longdesc>
+ <shortdesc lang="en">How the cluster should allocate resources to nodes</shortdesc>
+ <content type="select" default="">
+ <option value="default"/>
+ <option value="utilization"/>
+ <option value="minimal"/>
+ <option value="balanced"/>
+ </content>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List all available cluster options (XML) - OK (0) =#=#=#=
+* Passed: crm_attribute - List all available cluster options (XML)
=#=#=#= Begin test: Query the value of an attribute that does not exist =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query the value of an attribute that does not exist - No such object (105) =#=#=#=
@@ -33,7 +937,7 @@ crm_attribute: Error performing operation: No such device or address
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
- <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="5"/>
</cluster_property_set>
</crm_config>
<nodes/>
@@ -44,14 +948,230 @@ crm_attribute: Error performing operation: No such device or address
</cib>
=#=#=#= End test: Configure something before erasing - OK (0) =#=#=#=
* Passed: crm_attribute - Configure something before erasing
+=#=#=#= Begin test: Test '++' XML attribute update syntax =#=#=#=
+=#=#=#= Current cib after: Test '++' XML attribute update syntax =#=#=#=
+<cib epoch="2" num_updates="1" admin_epoch="1">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="5"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' XML attribute update syntax - OK (0) =#=#=#=
+* Passed: cibadmin - Test '++' XML attribute update syntax
+=#=#=#= Begin test: Test '+=' XML attribute update syntax =#=#=#=
+=#=#=#= Current cib after: Test '+=' XML attribute update syntax =#=#=#=
+<cib epoch="2" num_updates="2" admin_epoch="3">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="5"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' XML attribute update syntax - OK (0) =#=#=#=
+* Passed: cibadmin - Test '+=' XML attribute update syntax
+=#=#=#= Begin test: Test '++' nvpair value update syntax =#=#=#=
+=#=#=#= Current cib after: Test '++' nvpair value update syntax =#=#=#=
+<cib epoch="3" num_updates="0" admin_epoch="3">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="6"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' nvpair value update syntax - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '++' nvpair value update syntax
+=#=#=#= Begin test: Test '++' nvpair value update syntax (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute -n test_attr -v value++ --score --output-as=xml">
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= Current cib after: Test '++' nvpair value update syntax (XML) =#=#=#=
+<cib epoch="4" num_updates="0" admin_epoch="3">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="7"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' nvpair value update syntax (XML) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '++' nvpair value update syntax (XML)
+=#=#=#= Begin test: Test '+=' nvpair value update syntax =#=#=#=
+=#=#=#= Current cib after: Test '+=' nvpair value update syntax =#=#=#=
+<cib epoch="5" num_updates="0" admin_epoch="3">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="9"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' nvpair value update syntax - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '+=' nvpair value update syntax
+=#=#=#= Begin test: Test '+=' nvpair value update syntax (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute -n test_attr -v value+=2 --score --output-as=xml">
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= Current cib after: Test '+=' nvpair value update syntax (XML) =#=#=#=
+<cib epoch="6" num_updates="0" admin_epoch="3">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="11"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' nvpair value update syntax (XML) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '+=' nvpair value update syntax (XML)
+=#=#=#= Begin test: Test '++' XML attribute update syntax (--score not set) =#=#=#=
+=#=#=#= Current cib after: Test '++' XML attribute update syntax (--score not set) =#=#=#=
+<cib epoch="6" num_updates="1" admin_epoch="4">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="11"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' XML attribute update syntax (--score not set) - OK (0) =#=#=#=
+* Passed: cibadmin - Test '++' XML attribute update syntax (--score not set)
+=#=#=#= Begin test: Test '+=' XML attribute update syntax (--score not set) =#=#=#=
+=#=#=#= Current cib after: Test '+=' XML attribute update syntax (--score not set) =#=#=#=
+<cib epoch="6" num_updates="2" admin_epoch="6">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="11"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' XML attribute update syntax (--score not set) - OK (0) =#=#=#=
+* Passed: cibadmin - Test '+=' XML attribute update syntax (--score not set)
+=#=#=#= Begin test: Test '++' nvpair value update syntax (--score not set) =#=#=#=
+=#=#=#= Current cib after: Test '++' nvpair value update syntax (--score not set) =#=#=#=
+<cib epoch="7" num_updates="0" admin_epoch="6">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="12"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' nvpair value update syntax (--score not set) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '++' nvpair value update syntax (--score not set)
+=#=#=#= Begin test: Test '++' nvpair value update syntax (--score not set) (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute -n test_attr -v value++ --output-as=xml">
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= Current cib after: Test '++' nvpair value update syntax (--score not set) (XML) =#=#=#=
+<cib epoch="8" num_updates="0" admin_epoch="6">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="13"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '++' nvpair value update syntax (--score not set) (XML) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '++' nvpair value update syntax (--score not set) (XML)
+=#=#=#= Begin test: Test '+=' nvpair value update syntax (--score not set) =#=#=#=
+=#=#=#= Current cib after: Test '+=' nvpair value update syntax (--score not set) =#=#=#=
+<cib epoch="9" num_updates="0" admin_epoch="6">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="15"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' nvpair value update syntax (--score not set) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '+=' nvpair value update syntax (--score not set)
+=#=#=#= Begin test: Test '+=' nvpair value update syntax (--score not set) (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_attribute -n test_attr -v value+=2 --output-as=xml">
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= Current cib after: Test '+=' nvpair value update syntax (--score not set) (XML) =#=#=#=
+<cib epoch="10" num_updates="0" admin_epoch="6">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="17"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes/>
+ <resources/>
+ <constraints/>
+ </configuration>
+ <status/>
+</cib>
+=#=#=#= End test: Test '+=' nvpair value update syntax (--score not set) (XML) - OK (0) =#=#=#=
+* Passed: crm_attribute - Test '+=' nvpair value update syntax (--score not set) (XML)
=#=#=#= Begin test: Require --force for CIB erasure =#=#=#=
cibadmin: The supplied command is considered dangerous. To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= Current cib after: Require --force for CIB erasure =#=#=#=
-<cib epoch="2" num_updates="0" admin_epoch="0">
+<cib epoch="10" num_updates="0" admin_epoch="6">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
- <nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
+ <nvpair id="cib-bootstrap-options-test_attr" name="test_attr" value="17"/>
</cluster_property_set>
</crm_config>
<nodes/>
@@ -177,7 +1297,7 @@ cibadmin: The supplied command is considered dangerous. To prevent accidental de
=#=#=#= Begin test: Create operation should fail =#=#=#=
Call failed: File exists
<failed>
- <failed_update id="cib-bootstrap-options" object_type="cluster_property_set" operation="cib_create" reason="File exists">
+ <failed_update id="cib-bootstrap-options" object-type="cluster_property_set" operation="cib_create" reason="File exists">
<cluster_property_set id="cib-bootstrap-options">
<nvpair id="cib-bootstrap-options-cluster-delay" name="cluster-delay" value="60s"/>
</cluster_property_set>
@@ -461,8 +1581,8 @@ Revised Cluster Status:
=#=#=#= End test: Create second node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Create second node attribute
=#=#=#= Begin test: Query node attributes by pattern =#=#=#=
-scope=nodes name=ram value=1024M
-scope=nodes name=rattr value=XYZ
+scope=nodes name=ram value=1024M
+scope=nodes name=rattr value=XYZ
=#=#=#= End test: Query node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Query node attributes by pattern
=#=#=#= Begin test: Update node attributes by pattern =#=#=#=
@@ -644,8 +1764,8 @@ Current cluster status:
=#=#=#= End test: Set a second transient node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Set a second transient node attribute
=#=#=#= Begin test: Query transient node attributes by pattern =#=#=#=
-scope=status name=fail-count-foo value=3
-scope=status name=fail-count-bar value=5
+scope=status name=fail-count-foo value=3
+scope=status name=fail-count-bar value=5
=#=#=#= End test: Query transient node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Query transient node attributes by pattern
=#=#=#= Begin test: Update transient node attributes by pattern =#=#=#=
@@ -757,7 +1877,7 @@ crm_attribute: Error: must specify attribute name or pattern to delete
=#=#=#= End test: Set a utilization node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Set a utilization node attribute
=#=#=#= Begin test: Query utilization node attribute =#=#=#=
-scope=nodes name=cpu value=1
+scope=nodes name=cpu value=1
=#=#=#= End test: Query utilization node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Query utilization node attribute
=#=#=#= Begin test: Digest calculation =#=#=#=
@@ -832,7 +1952,7 @@ Call failed: Update was older than existing configuration
=#=#=#= End test: Replace operation should fail - Update was older than existing configuration (103) =#=#=#=
* Passed: cibadmin - Replace operation should fail
=#=#=#= Begin test: Default standby value =#=#=#=
-scope=status name=standby value=off
+scope=status name=standby value=off
=#=#=#= Current cib after: Default standby value =#=#=#=
<cib epoch="14" num_updates="0" admin_epoch="0">
<configuration>
@@ -904,7 +2024,7 @@ scope=status name=standby value=off
=#=#=#= End test: Set standby status - OK (0) =#=#=#=
* Passed: crm_standby - Set standby status
=#=#=#= Begin test: Query standby value =#=#=#=
-scope=nodes name=standby value=true
+scope=nodes name=standby value=true
=#=#=#= Current cib after: Query standby value =#=#=#=
<cib epoch="15" num_updates="0" admin_epoch="0">
<configuration>
@@ -1020,6 +2140,824 @@ crm_resource: non-option ARGV-elements:
=#=#=#= End test: crm_resource run with extra arguments - Incorrect usage (64) =#=#=#=
* Passed: crm_resource - crm_resource run with extra arguments
+=#=#=#= Begin test: List all available resource options (invalid type) =#=#=#=
+crm_resource: Error parsing option --list-options
+=#=#=#= End test: List all available resource options (invalid type) - Incorrect usage (64) =#=#=#=
+* Passed: crm_resource - List all available resource options (invalid type)
+=#=#=#= Begin test: List all available resource options (invalid type) (XML) =#=#=#=
+crm_resource: Error parsing option --list-options
+=#=#=#= End test: List all available resource options (invalid type) (XML) - Incorrect usage (64) =#=#=#=
+* Passed: crm_resource - List all available resource options (invalid type) (XML)
+=#=#=#= Begin test: List non-advanced primitive meta-attributes =#=#=#=
+Primitive meta-attributes
+
+Meta-attributes applicable to primitive resources
+
+ * priority: Resource assignment priority
+ * If not all resources can be active, the cluster will stop lower-priority resources in order to keep higher-priority ones active.
+ * Possible values: score (default: )
+
+ * critical: Default value for influence in colocation constraints
+ * 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.
+ * Possible values: boolean (default: )
+
+ * target-role: State the cluster should attempt to keep this resource in
+ * "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".
+ * Possible values: "Stopped", "Started" (default), "Unpromoted", "Promoted"
+
+ * is-managed: Whether the cluster is allowed to actively change the resource's state
+ * 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.
+ * Possible values: boolean (default: )
+
+ * maintenance: If true, the cluster will not schedule any actions involving the resource
+ * 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.
+ * Possible values: boolean (default: )
+
+ * resource-stickiness: Score to add to the current node when a resource is already active
+ * 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.
+ * Possible values: score (no default)
+
+ * requires: Conditions under which the resource can be started
+ * 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".
+ * Possible values: "nothing", "quorum", "fencing", "unfencing"
+
+ * migration-threshold: Number of failures on a node before the resource becomes ineligible to run there.
+ * 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.
+ * Possible values: score (default: )
+
+ * failure-timeout: Number of seconds before acting as if a failure had not occurred
+ * 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.
+ * Possible values: duration (default: )
+
+ * multiple-active: What to do if the cluster finds the resource active on more than one node
+ * 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.)
+ * Possible values: "block", "stop_only", "stop_start" (default), "stop_unexpected"
+
+ * allow-migrate: Whether the cluster should try to "live migrate" this resource when it needs to be moved
+ * 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.
+ * Possible values: boolean (no default)
+
+ * allow-unhealthy-nodes: Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it
+ * Possible values: boolean (default: )
+
+ * container-attribute-target: Where to check user-defined node attributes
+ * 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).
+ * Possible values: string (no default)
+
+ * remote-node: Name of the Pacemaker Remote guest node this resource is associated with, if any
+ * 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.
+ * Possible values: string (no default)
+
+ * remote-addr: If remote-node is specified, the IP address or hostname used to connect to the guest via Pacemaker Remote
+ * 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.
+ * Possible values: string (no default)
+
+ * remote-port: If remote-node is specified, port on the guest used for its Pacemaker Remote connection
+ * 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.
+ * Possible values: port (default: )
+
+ * remote-connect-timeout: If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.
+ * Possible values: timeout (default: )
+
+ * remote-allow-migrate: If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).
+ * Possible values: boolean (default: )
+=#=#=#= End test: List non-advanced primitive meta-attributes - OK (0) =#=#=#=
+* Passed: crm_resource - List non-advanced primitive meta-attributes
+=#=#=#= Begin test: List non-advanced primitive meta-attributes (XML) (shows all) =#=#=#=
+<pacemaker-result api-version="X" request="crm_resource --list-options=primitive --output-as=xml">
+ <resource-agent name="primitive-meta" version="">
+ <version>1.1</version>
+ <longdesc lang="en">Meta-attributes applicable to primitive resources</longdesc>
+ <shortdesc lang="en">Primitive meta-attributes</shortdesc>
+ <parameters>
+ <parameter name="priority" advanced="0" generated="0">
+ <longdesc lang="en">If not all resources can be active, the cluster will stop lower-priority resources in order to keep higher-priority ones active.</longdesc>
+ <shortdesc lang="en">Resource assignment priority</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="critical" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Default value for influence in colocation constraints</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="target-role" advanced="0" generated="0">
+ <longdesc lang="en">"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".</longdesc>
+ <shortdesc lang="en">State the cluster should attempt to keep this resource in</shortdesc>
+ <content type="select" default="">
+ <option value="Stopped"/>
+ <option value="Started"/>
+ <option value="Unpromoted"/>
+ <option value="Promoted"/>
+ </content>
+ </parameter>
+ <parameter name="is-managed" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether the cluster is allowed to actively change the resource's state</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="maintenance" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If true, the cluster will not schedule any actions involving the resource</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="resource-stickiness" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Score to add to the current node when a resource is already active</shortdesc>
+ <content type="score"/>
+ </parameter>
+ <parameter name="requires" advanced="0" generated="0">
+ <longdesc lang="en">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".</longdesc>
+ <shortdesc lang="en">Conditions under which the resource can be started</shortdesc>
+ <content type="select">
+ <option value="nothing"/>
+ <option value="quorum"/>
+ <option value="fencing"/>
+ <option value="unfencing"/>
+ </content>
+ </parameter>
+ <parameter name="migration-threshold" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Number of failures on a node before the resource becomes ineligible to run there.</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="failure-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Number of seconds before acting as if a failure had not occurred</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="multiple-active" advanced="0" generated="0">
+ <longdesc lang="en">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.)</longdesc>
+ <shortdesc lang="en">What to do if the cluster finds the resource active on more than one node</shortdesc>
+ <content type="select" default="">
+ <option value="block"/>
+ <option value="stop_only"/>
+ <option value="stop_start"/>
+ <option value="stop_unexpected"/>
+ </content>
+ </parameter>
+ <parameter name="allow-migrate" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether the cluster should try to "live migrate" this resource when it needs to be moved</shortdesc>
+ <content type="boolean"/>
+ </parameter>
+ <parameter name="allow-unhealthy-nodes" advanced="0" generated="0">
+ <longdesc lang="en">Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it</longdesc>
+ <shortdesc lang="en">Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="container-attribute-target" advanced="0" generated="0">
+ <longdesc lang="en">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).</longdesc>
+ <shortdesc lang="en">Where to check user-defined node attributes</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-node" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Name of the Pacemaker Remote guest node this resource is associated with, if any</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-addr" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, the IP address or hostname used to connect to the guest via Pacemaker Remote</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-port" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, port on the guest used for its Pacemaker Remote connection</shortdesc>
+ <content type="port" default=""/>
+ </parameter>
+ <parameter name="remote-connect-timeout" advanced="0" generated="0">
+ <longdesc lang="en">If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="remote-allow-migrate" advanced="0" generated="0">
+ <longdesc lang="en">If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).</longdesc>
+ <shortdesc lang="en">If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List non-advanced primitive meta-attributes (XML) (shows all) - OK (0) =#=#=#=
+* Passed: crm_resource - List non-advanced primitive meta-attributes (XML) (shows all)
+=#=#=#= Begin test: List all available primitive meta-attributes =#=#=#=
+Primitive meta-attributes
+
+Meta-attributes applicable to primitive resources
+
+ * priority: Resource assignment priority
+ * If not all resources can be active, the cluster will stop lower-priority resources in order to keep higher-priority ones active.
+ * Possible values: score (default: )
+
+ * critical: Default value for influence in colocation constraints
+ * 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.
+ * Possible values: boolean (default: )
+
+ * target-role: State the cluster should attempt to keep this resource in
+ * "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".
+ * Possible values: "Stopped", "Started" (default), "Unpromoted", "Promoted"
+
+ * is-managed: Whether the cluster is allowed to actively change the resource's state
+ * 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.
+ * Possible values: boolean (default: )
+
+ * maintenance: If true, the cluster will not schedule any actions involving the resource
+ * 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.
+ * Possible values: boolean (default: )
+
+ * resource-stickiness: Score to add to the current node when a resource is already active
+ * 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.
+ * Possible values: score (no default)
+
+ * requires: Conditions under which the resource can be started
+ * 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".
+ * Possible values: "nothing", "quorum", "fencing", "unfencing"
+
+ * migration-threshold: Number of failures on a node before the resource becomes ineligible to run there.
+ * 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.
+ * Possible values: score (default: )
+
+ * failure-timeout: Number of seconds before acting as if a failure had not occurred
+ * 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.
+ * Possible values: duration (default: )
+
+ * multiple-active: What to do if the cluster finds the resource active on more than one node
+ * 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.)
+ * Possible values: "block", "stop_only", "stop_start" (default), "stop_unexpected"
+
+ * allow-migrate: Whether the cluster should try to "live migrate" this resource when it needs to be moved
+ * 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.
+ * Possible values: boolean (no default)
+
+ * allow-unhealthy-nodes: Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it
+ * Possible values: boolean (default: )
+
+ * container-attribute-target: Where to check user-defined node attributes
+ * 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).
+ * Possible values: string (no default)
+
+ * remote-node: Name of the Pacemaker Remote guest node this resource is associated with, if any
+ * 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.
+ * Possible values: string (no default)
+
+ * remote-addr: If remote-node is specified, the IP address or hostname used to connect to the guest via Pacemaker Remote
+ * 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.
+ * Possible values: string (no default)
+
+ * remote-port: If remote-node is specified, port on the guest used for its Pacemaker Remote connection
+ * 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.
+ * Possible values: port (default: )
+
+ * remote-connect-timeout: If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.
+ * Possible values: timeout (default: )
+
+ * remote-allow-migrate: If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).
+ * Possible values: boolean (default: )
+=#=#=#= End test: List all available primitive meta-attributes - OK (0) =#=#=#=
+* Passed: crm_resource - List all available primitive meta-attributes
+=#=#=#= Begin test: List all available primitive meta-attributes (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_resource --list-options=primitive --all --output-as=xml">
+ <resource-agent name="primitive-meta" version="">
+ <version>1.1</version>
+ <longdesc lang="en">Meta-attributes applicable to primitive resources</longdesc>
+ <shortdesc lang="en">Primitive meta-attributes</shortdesc>
+ <parameters>
+ <parameter name="priority" advanced="0" generated="0">
+ <longdesc lang="en">If not all resources can be active, the cluster will stop lower-priority resources in order to keep higher-priority ones active.</longdesc>
+ <shortdesc lang="en">Resource assignment priority</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="critical" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Default value for influence in colocation constraints</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="target-role" advanced="0" generated="0">
+ <longdesc lang="en">"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".</longdesc>
+ <shortdesc lang="en">State the cluster should attempt to keep this resource in</shortdesc>
+ <content type="select" default="">
+ <option value="Stopped"/>
+ <option value="Started"/>
+ <option value="Unpromoted"/>
+ <option value="Promoted"/>
+ </content>
+ </parameter>
+ <parameter name="is-managed" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether the cluster is allowed to actively change the resource's state</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="maintenance" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If true, the cluster will not schedule any actions involving the resource</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="resource-stickiness" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Score to add to the current node when a resource is already active</shortdesc>
+ <content type="score"/>
+ </parameter>
+ <parameter name="requires" advanced="0" generated="0">
+ <longdesc lang="en">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".</longdesc>
+ <shortdesc lang="en">Conditions under which the resource can be started</shortdesc>
+ <content type="select">
+ <option value="nothing"/>
+ <option value="quorum"/>
+ <option value="fencing"/>
+ <option value="unfencing"/>
+ </content>
+ </parameter>
+ <parameter name="migration-threshold" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Number of failures on a node before the resource becomes ineligible to run there.</shortdesc>
+ <content type="score" default=""/>
+ </parameter>
+ <parameter name="failure-timeout" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Number of seconds before acting as if a failure had not occurred</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="multiple-active" advanced="0" generated="0">
+ <longdesc lang="en">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.)</longdesc>
+ <shortdesc lang="en">What to do if the cluster finds the resource active on more than one node</shortdesc>
+ <content type="select" default="">
+ <option value="block"/>
+ <option value="stop_only"/>
+ <option value="stop_start"/>
+ <option value="stop_unexpected"/>
+ </content>
+ </parameter>
+ <parameter name="allow-migrate" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Whether the cluster should try to "live migrate" this resource when it needs to be moved</shortdesc>
+ <content type="boolean"/>
+ </parameter>
+ <parameter name="allow-unhealthy-nodes" advanced="0" generated="0">
+ <longdesc lang="en">Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it</longdesc>
+ <shortdesc lang="en">Whether the resource should be allowed to run on a node even if the node's health score would otherwise prevent it</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ <parameter name="container-attribute-target" advanced="0" generated="0">
+ <longdesc lang="en">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).</longdesc>
+ <shortdesc lang="en">Where to check user-defined node attributes</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-node" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Name of the Pacemaker Remote guest node this resource is associated with, if any</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-addr" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, the IP address or hostname used to connect to the guest via Pacemaker Remote</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="remote-port" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, port on the guest used for its Pacemaker Remote connection</shortdesc>
+ <content type="port" default=""/>
+ </parameter>
+ <parameter name="remote-connect-timeout" advanced="0" generated="0">
+ <longdesc lang="en">If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.</longdesc>
+ <shortdesc lang="en">If remote-node is specified, how long before a pending Pacemaker Remote guest connection times out.</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="remote-allow-migrate" advanced="0" generated="0">
+ <longdesc lang="en">If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).</longdesc>
+ <shortdesc lang="en">If remote-node is specified, this acts as the allow-migrate meta-attribute for the implicit remote connection resource (ocf:pacemaker:remote).</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List all available primitive meta-attributes (XML) - OK (0) =#=#=#=
+* Passed: crm_resource - List all available primitive meta-attributes (XML)
+=#=#=#= Begin test: List non-advanced fencing parameters =#=#=#=
+Fencing resource common parameters
+
+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.
+
+ * pcmk_host_map: A mapping of node names to port numbers for devices that do not support node names.
+ * For example, "node1:1;node2:2,3" would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2.
+ * Possible values: string (no default)
+
+ * pcmk_host_list: Nodes targeted by this device
+ * 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.
+ * Possible values: string (no default)
+
+ * pcmk_host_check: How to determine which nodes can be targeted by the device
+ * 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"
+ * Possible values: "dynamic-list", "static-list", "status", "none"
+
+ * pcmk_delay_max: Enable a delay of no more than the time specified before executing fencing actions.
+ * 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.
+ * Possible values: duration (default: )
+
+ * pcmk_delay_base: Enable a base delay for fencing actions and specify base delay value.
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_action_limit: The maximum number of actions can be performed in parallel on this device
+ * 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.
+ * Possible values: integer (default: )
+=#=#=#= End test: List non-advanced fencing parameters - OK (0) =#=#=#=
+* Passed: crm_resource - List non-advanced fencing parameters
+=#=#=#= Begin test: List non-advanced fencing parameters (XML) (shows all) =#=#=#=
+<pacemaker-result api-version="X" request="crm_resource --list-options=fencing --output-as=xml">
+ <resource-agent name="fence-attributes" version="">
+ <version>1.1</version>
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Fencing resource common parameters</shortdesc>
+ <parameters>
+ <parameter name="pcmk_host_argument" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate parameter to supply instead of 'port'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_host_map" advanced="0" generated="0">
+ <longdesc lang="en">For example, "node1:1;node2:2,3" would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2.</longdesc>
+ <shortdesc lang="en">A mapping of node names to port numbers for devices that do not support node names.</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="pcmk_host_list" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Nodes targeted by this device</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="pcmk_host_check" advanced="0" generated="0">
+ <longdesc lang="en">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"</longdesc>
+ <shortdesc lang="en">How to determine which nodes can be targeted by the device</shortdesc>
+ <content type="select">
+ <option value="dynamic-list"/>
+ <option value="static-list"/>
+ <option value="status"/>
+ <option value="none"/>
+ </content>
+ </parameter>
+ <parameter name="pcmk_delay_max" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enable a delay of no more than the time specified before executing fencing actions.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="pcmk_delay_base" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enable a base delay for fencing actions and specify base delay value.</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_action_limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of actions can be performed in parallel on this device</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'reboot'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'reboot' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'reboot' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'reboot' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'off'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'off' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'off' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'off' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'on'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'on' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'on' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'on' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'list'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'list' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'list' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'list' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'monitor'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'monitor' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'monitor' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'monitor' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'status'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'status' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'status' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'status' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List non-advanced fencing parameters (XML) (shows all) - OK (0) =#=#=#=
+* Passed: crm_resource - List non-advanced fencing parameters (XML) (shows all)
+=#=#=#= Begin test: List all available fencing parameters =#=#=#=
+Fencing resource common parameters
+
+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.
+
+ * pcmk_host_map: A mapping of node names to port numbers for devices that do not support node names.
+ * For example, "node1:1;node2:2,3" would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2.
+ * Possible values: string (no default)
+
+ * pcmk_host_list: Nodes targeted by this device
+ * 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.
+ * Possible values: string (no default)
+
+ * pcmk_host_check: How to determine which nodes can be targeted by the device
+ * 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"
+ * Possible values: "dynamic-list", "static-list", "status", "none"
+
+ * pcmk_delay_max: Enable a delay of no more than the time specified before executing fencing actions.
+ * 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.
+ * Possible values: duration (default: )
+
+ * pcmk_delay_base: Enable a base delay for fencing actions and specify base delay value.
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_action_limit: The maximum number of actions can be performed in parallel on this device
+ * 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.
+ * Possible values: integer (default: )
+
+ * ADVANCED OPTIONS:
+
+ * pcmk_host_argument: An alternate parameter to supply instead of 'port'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_reboot_action: An alternate command to run instead of 'reboot'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_reboot_timeout: Specify an alternate timeout to use for 'reboot' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'reboot' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_reboot_retries: The maximum number of times to try the 'reboot' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+
+ * pcmk_off_action: An alternate command to run instead of 'off'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_off_timeout: Specify an alternate timeout to use for 'off' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'off' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_off_retries: The maximum number of times to try the 'off' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+
+ * pcmk_on_action: An alternate command to run instead of 'on'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_on_timeout: Specify an alternate timeout to use for 'on' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'on' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_on_retries: The maximum number of times to try the 'on' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+
+ * pcmk_list_action: An alternate command to run instead of 'list'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_list_timeout: Specify an alternate timeout to use for 'list' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'list' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_list_retries: The maximum number of times to try the 'list' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+
+ * pcmk_monitor_action: An alternate command to run instead of 'monitor'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_monitor_timeout: Specify an alternate timeout to use for 'monitor' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'monitor' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_monitor_retries: The maximum number of times to try the 'monitor' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+
+ * pcmk_status_action: An alternate command to run instead of 'status'
+ * 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.
+ * Possible values: string (default: )
+
+ * pcmk_status_timeout: Specify an alternate timeout to use for 'status' actions instead of stonith-timeout
+ * Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'status' actions.
+ * Possible values: timeout (default: )
+
+ * pcmk_status_retries: The maximum number of times to try the 'status' command within the timeout period
+ * 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.
+ * Possible values: integer (default: )
+=#=#=#= End test: List all available fencing parameters - OK (0) =#=#=#=
+* Passed: crm_resource - List all available fencing parameters
+=#=#=#= Begin test: List all available fencing parameters (XML) =#=#=#=
+<pacemaker-result api-version="X" request="crm_resource --list-options=fencing --all --output-as=xml">
+ <resource-agent name="fence-attributes" version="">
+ <version>1.1</version>
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Fencing resource common parameters</shortdesc>
+ <parameters>
+ <parameter name="pcmk_host_argument" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate parameter to supply instead of 'port'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_host_map" advanced="0" generated="0">
+ <longdesc lang="en">For example, "node1:1;node2:2,3" would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2.</longdesc>
+ <shortdesc lang="en">A mapping of node names to port numbers for devices that do not support node names.</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="pcmk_host_list" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Nodes targeted by this device</shortdesc>
+ <content type="string"/>
+ </parameter>
+ <parameter name="pcmk_host_check" advanced="0" generated="0">
+ <longdesc lang="en">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"</longdesc>
+ <shortdesc lang="en">How to determine which nodes can be targeted by the device</shortdesc>
+ <content type="select">
+ <option value="dynamic-list"/>
+ <option value="static-list"/>
+ <option value="status"/>
+ <option value="none"/>
+ </content>
+ </parameter>
+ <parameter name="pcmk_delay_max" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enable a delay of no more than the time specified before executing fencing actions.</shortdesc>
+ <content type="duration" default=""/>
+ </parameter>
+ <parameter name="pcmk_delay_base" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">Enable a base delay for fencing actions and specify base delay value.</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_action_limit" advanced="0" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of actions can be performed in parallel on this device</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'reboot'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'reboot' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'reboot' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_reboot_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'reboot' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'off'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'off' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'off' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_off_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'off' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'on'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'on' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'on' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_on_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'on' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'list'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'list' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'list' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_list_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'list' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'monitor'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'monitor' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'monitor' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_monitor_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'monitor' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_action" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">An alternate command to run instead of 'status'</shortdesc>
+ <content type="string" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_timeout" advanced="1" generated="0">
+ <longdesc lang="en">Some devices need much more/less time to complete than normal. Use this to specify an alternate, device-specific, timeout for 'status' actions.</longdesc>
+ <shortdesc lang="en">Specify an alternate timeout to use for 'status' actions instead of stonith-timeout</shortdesc>
+ <content type="timeout" default=""/>
+ </parameter>
+ <parameter name="pcmk_status_retries" advanced="1" generated="0">
+ <longdesc lang="en">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.</longdesc>
+ <shortdesc lang="en">The maximum number of times to try the 'status' command within the timeout period</shortdesc>
+ <content type="integer" default=""/>
+ </parameter>
+ </parameters>
+ </resource-agent>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List all available fencing parameters (XML) - OK (0) =#=#=#=
+* Passed: crm_resource - List all available fencing parameters (XML)
=#=#=#= Begin test: crm_resource given both -r and resource config =#=#=#=
crm_resource: --resource cannot be used with --class, --agent, and --provider
=#=#=#= End test: crm_resource given both -r and resource config - Incorrect usage (64) =#=#=#=
@@ -1166,6 +3104,13 @@ unpack_resources error: Resource start-up disabled since no STONITH resources h
unpack_resources error: Either configure some or disable STONITH with the stonith-enabled option
unpack_resources error: NOTE: Clusters with shared data need STONITH to ensure data integrity
<pacemaker-result api-version="X" request="crm_resource -r dummy --meta -p target-role -v Stopped --output-as=xml">
+ <resource-settings>
+ <primitive id="dummy">
+ <meta_attributes id="dummy-meta_attributes">
+ <nvpair id="dummy-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ </resource-settings>
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= End test: Create another resource meta attribute - OK (0) =#=#=#=
@@ -2083,7 +4028,7 @@ Error performing operation: No such object
* Passed: crm_resource - Try to move a resource that doesn't exist
=#=#=#= Begin test: Move a resource from its existing location =#=#=#=
WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score of -INFINITY for resource dummy on node1.
- This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool
+ This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool.
This will be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Move a resource from its existing location =#=#=#=
<cib epoch="28" num_updates="0" admin_epoch="0">
@@ -2303,6 +4248,34 @@ false
</cib>
=#=#=#= End test: Set ticket granted state - OK (0) =#=#=#=
* Passed: crm_ticket - Set ticket granted state
+=#=#=#= Begin test: List ticket IDs =#=#=#=
+ticketA
+=#=#=#= End test: List ticket IDs - OK (0) =#=#=#=
+* Passed: crm_ticket - List ticket IDs
+=#=#=#= Begin test: List ticket IDs, outputting in XML =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -w --output-as=xml">
+ <tickets>
+ <ticket id="ticketA" status="revoked" standby="false" granted="false"/>
+ </tickets>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List ticket IDs, outputting in XML - OK (0) =#=#=#=
+* Passed: crm_ticket - List ticket IDs, outputting in XML
+=#=#=#= Begin test: Query ticket state =#=#=#=
+State XML:
+
+<ticket_state id="ticketA" granted="false"/>
+=#=#=#= End test: Query ticket state - OK (0) =#=#=#=
+* Passed: crm_ticket - Query ticket state
+=#=#=#= Begin test: Query ticket state, outputting as xml =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -t ticketA -q --output-as=xml">
+ <tickets>
+ <ticket id="ticketA" granted="false"/>
+ </tickets>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: Query ticket state, outputting as xml - OK (0) =#=#=#=
+* Passed: crm_ticket - Query ticket state, outputting as xml
=#=#=#= Begin test: Query ticket granted state =#=#=#=
false
=#=#=#= Current cib after: Query ticket granted state =#=#=#=
@@ -2360,6 +4333,17 @@ false
</cib>
=#=#=#= End test: Query ticket granted state - OK (0) =#=#=#=
* Passed: crm_ticket - Query ticket granted state
+=#=#=#= Begin test: Query ticket granted state, outputting as xml =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -t ticketA -G granted --output-as=xml">
+ <tickets>
+ <ticket id="ticketA">
+ <attribute name="granted" value="false"/>
+ </ticket>
+ </tickets>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: Query ticket granted state, outputting as xml - OK (0) =#=#=#=
+* Passed: crm_ticket - Query ticket granted state, outputting as xml
=#=#=#= Begin test: Delete ticket granted state =#=#=#=
=#=#=#= Current cib after: Delete ticket granted state =#=#=#=
<cib epoch="29" num_updates="2" admin_epoch="0">
@@ -2585,9 +4569,207 @@ true
</cib>
=#=#=#= End test: Activate a ticket - OK (0) =#=#=#=
* Passed: crm_ticket - Activate a ticket
+=#=#=#= Begin test: List ticket details =#=#=#=
+ticketA revoked (standby=false)
+=#=#=#= End test: List ticket details - OK (0) =#=#=#=
+* Passed: crm_ticket - List ticket details
+=#=#=#= Begin test: List ticket details, outputting as XML =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -L -t ticketA --output-as=xml">
+ <tickets>
+ <ticket id="ticketA" status="revoked" standby="false"/>
+ </tickets>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List ticket details, outputting as XML - OK (0) =#=#=#=
+* Passed: crm_ticket - List ticket details, outputting as XML
+=#=#=#= Begin test: Add a second ticket =#=#=#=
+false
+=#=#=#= Current cib after: Add a second ticket =#=#=#=
+<cib epoch="29" num_updates="4" admin_epoch="0">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ <cluster_property_set id="duplicate">
+ <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" uname="node1">
+ <instance_attributes id="nodes-node1">
+ <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+ </instance_attributes>
+ <utilization id="nodes-node1-utilization">
+ <nvpair id="nodes-node1-utilization-cpu" name="cpu" value="1"/>
+ </utilization>
+ </node>
+ </nodes>
+ <resources>
+ <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <meta_attributes id="dummy-meta_attributes"/>
+ <instance_attributes id="dummy-instance_attributes">
+ <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+ </instance_attributes>
+ </primitive>
+ <primitive id="Fence" class="stonith" type="fence_true"/>
+ </resources>
+ <constraints/>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate">
+ <transient_attributes id="node1">
+ <instance_attributes id="status-node1"/>
+ </transient_attributes>
+ <lrm id="node1">
+ <lrm_resources>
+ <lrm_resource id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <lrm_rsc_op id="dummy_last_0" operation_key="dummy_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="0830891652dabe627ca72b8e879199b1"/>
+ </lrm_resource>
+ <lrm_resource id="Fence" class="stonith" type="fence_true">
+ <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
+ </lrm_resource>
+ </lrm_resources>
+ </lrm>
+ </node_state>
+ <tickets>
+ <ticket_state id="ticketA" standby="false"/>
+ </tickets>
+ </status>
+</cib>
+=#=#=#= End test: Add a second ticket - OK (0) =#=#=#=
+* Passed: crm_ticket - Add a second ticket
+=#=#=#= Begin test: Set second ticket granted state =#=#=#=
+=#=#=#= Current cib after: Set second ticket granted state =#=#=#=
+<cib epoch="29" num_updates="5" admin_epoch="0">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ <cluster_property_set id="duplicate">
+ <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" uname="node1">
+ <instance_attributes id="nodes-node1">
+ <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+ </instance_attributes>
+ <utilization id="nodes-node1-utilization">
+ <nvpair id="nodes-node1-utilization-cpu" name="cpu" value="1"/>
+ </utilization>
+ </node>
+ </nodes>
+ <resources>
+ <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <meta_attributes id="dummy-meta_attributes"/>
+ <instance_attributes id="dummy-instance_attributes">
+ <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+ </instance_attributes>
+ </primitive>
+ <primitive id="Fence" class="stonith" type="fence_true"/>
+ </resources>
+ <constraints/>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate">
+ <transient_attributes id="node1">
+ <instance_attributes id="status-node1"/>
+ </transient_attributes>
+ <lrm id="node1">
+ <lrm_resources>
+ <lrm_resource id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <lrm_rsc_op id="dummy_last_0" operation_key="dummy_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="0830891652dabe627ca72b8e879199b1"/>
+ </lrm_resource>
+ <lrm_resource id="Fence" class="stonith" type="fence_true">
+ <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
+ </lrm_resource>
+ </lrm_resources>
+ </lrm>
+ </node_state>
+ <tickets>
+ <ticket_state id="ticketA" standby="false"/>
+ <ticket_state id="ticketB" granted="false"/>
+ </tickets>
+ </status>
+</cib>
+=#=#=#= End test: Set second ticket granted state - OK (0) =#=#=#=
+* Passed: crm_ticket - Set second ticket granted state
+=#=#=#= Begin test: List tickets =#=#=#=
+ticketA revoked
+ticketB revoked
+=#=#=#= End test: List tickets - OK (0) =#=#=#=
+* Passed: crm_ticket - List tickets
+=#=#=#= Begin test: List tickets, outputting as XML =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -l --output-as=xml">
+ <tickets>
+ <ticket id="ticketA" status="revoked" standby="false"/>
+ <ticket id="ticketB" status="revoked" standby="false" granted="false"/>
+ </tickets>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: List tickets, outputting as XML - OK (0) =#=#=#=
+* Passed: crm_ticket - List tickets, outputting as XML
+=#=#=#= Begin test: Delete second ticket =#=#=#=
+=#=#=#= Current cib after: Delete second ticket =#=#=#=
+<cib epoch="29" num_updates="6" admin_epoch="0">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ <cluster_property_set id="duplicate">
+ <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" uname="node1">
+ <instance_attributes id="nodes-node1">
+ <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+ </instance_attributes>
+ <utilization id="nodes-node1-utilization">
+ <nvpair id="nodes-node1-utilization-cpu" name="cpu" value="1"/>
+ </utilization>
+ </node>
+ </nodes>
+ <resources>
+ <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <meta_attributes id="dummy-meta_attributes"/>
+ <instance_attributes id="dummy-instance_attributes">
+ <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+ </instance_attributes>
+ </primitive>
+ <primitive id="Fence" class="stonith" type="fence_true"/>
+ </resources>
+ <constraints/>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate">
+ <transient_attributes id="node1">
+ <instance_attributes id="status-node1"/>
+ </transient_attributes>
+ <lrm id="node1">
+ <lrm_resources>
+ <lrm_resource id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <lrm_rsc_op id="dummy_last_0" operation_key="dummy_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="0830891652dabe627ca72b8e879199b1"/>
+ </lrm_resource>
+ <lrm_resource id="Fence" class="stonith" type="fence_true">
+ <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
+ </lrm_resource>
+ </lrm_resources>
+ </lrm>
+ </node_state>
+ <tickets>
+ <ticket_state id="ticketA" standby="false"/>
+ </tickets>
+ </status>
+</cib>
+=#=#=#= End test: Delete second ticket - OK (0) =#=#=#=
+* Passed: cibadmin - Delete second ticket
=#=#=#= Begin test: Delete ticket standby state =#=#=#=
=#=#=#= Current cib after: Delete ticket standby state =#=#=#=
-<cib epoch="29" num_updates="5" admin_epoch="0">
+<cib epoch="29" num_updates="7" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -2641,11 +4823,147 @@ true
</cib>
=#=#=#= End test: Delete ticket standby state - OK (0) =#=#=#=
* Passed: crm_ticket - Delete ticket standby state
+=#=#=#= Begin test: Delete ticket standby state =#=#=#=
+=#=#=#= Current cib after: Delete ticket standby state =#=#=#=
+<cib epoch="30" num_updates="0" admin_epoch="0">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ <cluster_property_set id="duplicate">
+ <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" uname="node1">
+ <instance_attributes id="nodes-node1">
+ <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+ </instance_attributes>
+ <utilization id="nodes-node1-utilization">
+ <nvpair id="nodes-node1-utilization-cpu" name="cpu" value="1"/>
+ </utilization>
+ </node>
+ </nodes>
+ <resources>
+ <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <meta_attributes id="dummy-meta_attributes"/>
+ <instance_attributes id="dummy-instance_attributes">
+ <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+ </instance_attributes>
+ </primitive>
+ <primitive id="Fence" class="stonith" type="fence_true"/>
+ </resources>
+ <constraints>
+ <rsc_ticket id="dummy-dep-ticketA" rsc="dummy" rsc-role="Started" ticket="ticketA" loss-policy="freeze"/>
+ </constraints>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate">
+ <transient_attributes id="node1">
+ <instance_attributes id="status-node1"/>
+ </transient_attributes>
+ <lrm id="node1">
+ <lrm_resources>
+ <lrm_resource id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <lrm_rsc_op id="dummy_last_0" operation_key="dummy_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="0830891652dabe627ca72b8e879199b1"/>
+ </lrm_resource>
+ <lrm_resource id="Fence" class="stonith" type="fence_true">
+ <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
+ </lrm_resource>
+ </lrm_resources>
+ </lrm>
+ </node_state>
+ <tickets>
+ <ticket_state id="ticketA"/>
+ </tickets>
+ </status>
+</cib>
+=#=#=#= End test: Delete ticket standby state - OK (0) =#=#=#=
+* Passed: cibadmin - Delete ticket standby state
+=#=#=#= Begin test: Query ticket constraints =#=#=#=
+Constraints XML:
+
+<rsc_ticket id="dummy-dep-ticketA" rsc="dummy" rsc-role="Started" ticket="ticketA" loss-policy="freeze"/>
+=#=#=#= End test: Query ticket constraints - OK (0) =#=#=#=
+* Passed: crm_ticket - Query ticket constraints
+=#=#=#= Begin test: Query ticket constraints, outputting as xml =#=#=#=
+<pacemaker-result api-version="X" request="crm_ticket -t ticketA -c --output-as=xml">
+ <tickets>
+ <ticket id="ticketA">
+ <constraints>
+ <rsc_ticket id="dummy-dep-ticketA" rsc="dummy" rsc-role="Started" ticket="ticketA" loss-policy="freeze"/>
+ </constraints>
+ </ticket>
+ </tickets>
+ <resources>
+ <resource id="dummy"/>
+ </resources>
+ <status code="0" message="OK"/>
+</pacemaker-result>
+=#=#=#= End test: Query ticket constraints, outputting as xml - OK (0) =#=#=#=
+* Passed: crm_ticket - Query ticket constraints, outputting as xml
+=#=#=#= Begin test: Delete ticket constraint =#=#=#=
+=#=#=#= Current cib after: Delete ticket constraint =#=#=#=
+<cib epoch="31" num_updates="0" admin_epoch="0">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ <cluster_property_set id="duplicate">
+ <nvpair id="duplicate-cluster-delay" name="cluster-delay" value="30s"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" uname="node1">
+ <instance_attributes id="nodes-node1">
+ <nvpair id="nodes-node1-ram" name="ram" value="1024M"/>
+ </instance_attributes>
+ <utilization id="nodes-node1-utilization">
+ <nvpair id="nodes-node1-utilization-cpu" name="cpu" value="1"/>
+ </utilization>
+ </node>
+ </nodes>
+ <resources>
+ <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <meta_attributes id="dummy-meta_attributes"/>
+ <instance_attributes id="dummy-instance_attributes">
+ <nvpair id="dummy-instance_attributes-delay" name="delay" value="10s"/>
+ </instance_attributes>
+ </primitive>
+ <primitive id="Fence" class="stonith" type="fence_true"/>
+ </resources>
+ <constraints/>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate">
+ <transient_attributes id="node1">
+ <instance_attributes id="status-node1"/>
+ </transient_attributes>
+ <lrm id="node1">
+ <lrm_resources>
+ <lrm_resource id="dummy" class="ocf" provider="pacemaker" type="Dummy">
+ <lrm_rsc_op id="dummy_last_0" operation_key="dummy_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="0830891652dabe627ca72b8e879199b1"/>
+ </lrm_resource>
+ <lrm_resource id="Fence" class="stonith" type="fence_true">
+ <lrm_rsc_op id="Fence_last_0" operation_key="Fence_start_0" operation="start" crm-debug-origin="crm_simulate" transition-key="2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" transition-magic="0:0;2:-1:0:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" exit-reason="" call-id="2" rc-code="0" op-status="0" interval="0" exec-time="0" queue-time="0" op-digest="f2317cad3d54cec5d7d7aa7d0bf35cf8"/>
+ </lrm_resource>
+ </lrm_resources>
+ </lrm>
+ </node_state>
+ <tickets>
+ <ticket_state id="ticketA"/>
+ </tickets>
+ </status>
+</cib>
+=#=#=#= End test: Delete ticket constraint - OK (0) =#=#=#=
+* Passed: cibadmin - Delete ticket constraint
=#=#=#= Begin test: Ban a resource on unknown node =#=#=#=
crm_resource: Node 'host1' not found
Error performing operation: No such object
=#=#=#= Current cib after: Ban a resource on unknown node =#=#=#=
-<cib epoch="29" num_updates="5" admin_epoch="0">
+<cib epoch="31" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -2731,7 +5049,7 @@ Revised Cluster Status:
* dummy (ocf:pacemaker:Dummy): Started node1
* Fence (stonith:fence_true): Started node2
=#=#=#= Current cib after: Create two more nodes and bring them online =#=#=#=
-<cib epoch="31" num_updates="8" admin_epoch="0">
+<cib epoch="33" num_updates="8" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -2813,10 +5131,10 @@ Revised Cluster Status:
* Passed: crm_simulate - Create two more nodes and bring them online
=#=#=#= Begin test: Ban dummy from node1 =#=#=#=
WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score of -INFINITY for resource dummy on node1.
- This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool
+ This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool.
This will be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Ban dummy from node1 =#=#=#=
-<cib epoch="32" num_updates="0" admin_epoch="0">
+<cib epoch="34" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -2912,7 +5230,7 @@ Locations:
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= Current cib after: Ban dummy from node2 =#=#=#=
-<cib epoch="33" num_updates="0" admin_epoch="0">
+<cib epoch="35" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3019,7 +5337,7 @@ Revised Cluster Status:
* dummy (ocf:pacemaker:Dummy): Started node3
* Fence (stonith:fence_true): Started node2
=#=#=#= Current cib after: Relocate resources due to ban =#=#=#=
-<cib epoch="33" num_updates="2" admin_epoch="0">
+<cib epoch="35" num_updates="2" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3107,7 +5425,7 @@ Revised Cluster Status:
<status code="0" message="OK"/>
</pacemaker-result>
=#=#=#= Current cib after: Move dummy to node1 =#=#=#=
-<cib epoch="35" num_updates="0" admin_epoch="0">
+<cib epoch="37" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3193,7 +5511,7 @@ Revised Cluster Status:
=#=#=#= Begin test: Clear implicit constraints for dummy on node2 =#=#=#=
Removing constraint: cli-ban-dummy-on-node2
=#=#=#= Current cib after: Clear implicit constraints for dummy on node2 =#=#=#=
-<cib epoch="36" num_updates="0" admin_epoch="0">
+<cib epoch="38" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3285,7 +5603,7 @@ Removing constraint: cli-ban-dummy-on-node2
Performing update of 'is-managed' on 'test-clone', the parent of 'test-primitive'
Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone-meta_attributes name=is-managed value=false
=#=#=#= Current cib after: Create a resource meta attribute =#=#=#=
-<cib epoch="38" num_updates="0" admin_epoch="0">
+<cib epoch="40" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3333,7 +5651,7 @@ Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone
=#=#=#= Begin test: Create a resource meta attribute in the primitive =#=#=#=
Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=test-primitive-meta_attributes name=is-managed value=false
=#=#=#= Current cib after: Create a resource meta attribute in the primitive =#=#=#=
-<cib epoch="39" num_updates="0" admin_epoch="0">
+<cib epoch="41" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3390,7 +5708,7 @@ Multiple attributes match name=is-managed
A value for 'is-managed' already exists in child 'test-primitive', performing update on that instead of 'test-clone'
Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=true
=#=#=#= Current cib after: Update resource meta attribute with duplicates =#=#=#=
-<cib epoch="40" num_updates="0" admin_epoch="0">
+<cib epoch="42" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3442,7 +5760,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=i
=#=#=#= Begin test: Update resource meta attribute with duplicates (force clone) =#=#=#=
Set 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed value=true
=#=#=#= Current cib after: Update resource meta attribute with duplicates (force clone) =#=#=#=
-<cib epoch="41" num_updates="0" admin_epoch="0">
+<cib epoch="43" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3498,7 +5816,7 @@ Multiple attributes match name=is-managed
Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=false
=#=#=#= Current cib after: Update child resource meta attribute with duplicates =#=#=#=
-<cib epoch="42" num_updates="0" admin_epoch="0">
+<cib epoch="44" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3555,7 +5873,7 @@ Multiple attributes match name=is-managed
A value for 'is-managed' already exists in child 'test-primitive', performing delete on that instead of 'test-clone'
Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Delete resource meta attribute with duplicates =#=#=#=
-<cib epoch="43" num_updates="0" admin_epoch="0">
+<cib epoch="45" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3606,7 +5924,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na
Performing delete of 'is-managed' on 'test-clone', the parent of 'test-primitive'
Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Delete resource meta attribute in parent =#=#=#=
-<cib epoch="44" num_updates="0" admin_epoch="0">
+<cib epoch="46" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3654,7 +5972,7 @@ Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-ma
=#=#=#= Begin test: Create a resource meta attribute in the primitive =#=#=#=
Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=test-primitive-meta_attributes name=is-managed value=false
=#=#=#= Current cib after: Create a resource meta attribute in the primitive =#=#=#=
-<cib epoch="45" num_updates="0" admin_epoch="0">
+<cib epoch="47" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3705,7 +6023,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed set=te
A value for 'is-managed' already exists in child 'test-primitive', performing update on that instead of 'test-clone'
Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed value=true
=#=#=#= Current cib after: Update existing resource meta attribute =#=#=#=
-<cib epoch="46" num_updates="0" admin_epoch="0">
+<cib epoch="48" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3755,7 +6073,7 @@ Set 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=i
=#=#=#= Begin test: Create a resource meta attribute in the parent =#=#=#=
Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone-meta_attributes name=is-managed value=true
=#=#=#= Current cib after: Create a resource meta attribute in the parent =#=#=#=
-<cib epoch="47" num_updates="0" admin_epoch="0">
+<cib epoch="49" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3810,7 +6128,7 @@ Set 'test-clone' option: id=test-clone-meta_attributes-is-managed set=test-clone
=#=#=#= Begin test: Delete resource parent meta attribute (force) =#=#=#=
Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Delete resource parent meta attribute (force) =#=#=#=
-<cib epoch="48" num_updates="0" admin_epoch="0">
+<cib epoch="50" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3859,7 +6177,7 @@ Deleted 'test-clone' option: id=test-clone-meta_attributes-is-managed name=is-ma
* Passed: crm_resource - Delete resource parent meta attribute (force)
=#=#=#= Begin test: Restore duplicates =#=#=#=
=#=#=#= Current cib after: Restore duplicates =#=#=#=
-<cib epoch="49" num_updates="0" admin_epoch="0">
+<cib epoch="51" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3915,7 +6233,7 @@ Multiple attributes match name=is-managed
Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Delete resource child meta attribute =#=#=#=
-<cib epoch="50" num_updates="0" admin_epoch="0">
+<cib epoch="52" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -3964,7 +6282,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na
* Passed: crm_resource - Delete resource child meta attribute
=#=#=#= Begin test: Create the dummy-group resource group =#=#=#=
=#=#=#= Current cib after: Create the dummy-group resource group =#=#=#=
-<cib epoch="51" num_updates="0" admin_epoch="0">
+<cib epoch="53" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4018,7 +6336,7 @@ Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed na
=#=#=#= Begin test: Create a resource meta attribute in dummy1 =#=#=#=
Set 'dummy1' option: id=dummy1-meta_attributes-is-managed set=dummy1-meta_attributes name=is-managed value=true
=#=#=#= Current cib after: Create a resource meta attribute in dummy1 =#=#=#=
-<cib epoch="52" num_updates="0" admin_epoch="0">
+<cib epoch="54" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4077,7 +6395,7 @@ Set 'dummy1' option: id=dummy1-meta_attributes-is-managed set=dummy1-meta_attrib
Set 'dummy1' option: id=dummy1-meta_attributes-is-managed name=is-managed value=false
Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-group-meta_attributes name=is-managed value=false
=#=#=#= Current cib after: Create a resource meta attribute in dummy-group =#=#=#=
-<cib epoch="54" num_updates="0" admin_epoch="0">
+<cib epoch="56" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4137,7 +6455,7 @@ Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-gr
* Passed: crm_resource - Create a resource meta attribute in dummy-group
=#=#=#= Begin test: Delete the dummy-group resource group =#=#=#=
=#=#=#= Current cib after: Delete the dummy-group resource group =#=#=#=
-<cib epoch="55" num_updates="0" admin_epoch="0">
+<cib epoch="57" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4187,7 +6505,7 @@ Set 'dummy-group' option: id=dummy-group-meta_attributes-is-managed set=dummy-gr
=#=#=#= Begin test: Specify a lifetime when moving a resource =#=#=#=
Migration will take effect until:
=#=#=#= Current cib after: Specify a lifetime when moving a resource =#=#=#=
-<cib epoch="57" num_updates="0" admin_epoch="0">
+<cib epoch="59" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4241,7 +6559,7 @@ Migration will take effect until:
* Passed: crm_resource - Specify a lifetime when moving a resource
=#=#=#= Begin test: Try to move a resource previously moved with a lifetime =#=#=#=
=#=#=#= Current cib after: Try to move a resource previously moved with a lifetime =#=#=#=
-<cib epoch="59" num_updates="0" admin_epoch="0">
+<cib epoch="61" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4291,10 +6609,10 @@ Migration will take effect until:
=#=#=#= Begin test: Ban dummy from node1 for a short time =#=#=#=
Migration will take effect until:
WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score of -INFINITY for resource dummy on node1.
- This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool
+ This will prevent dummy from running on node1 until the constraint is removed using the clear option or by editing the CIB with an appropriate tool.
This will be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Ban dummy from node1 for a short time =#=#=#=
-<cib epoch="60" num_updates="0" admin_epoch="0">
+<cib epoch="62" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4350,7 +6668,7 @@ WARNING: Creating rsc_location constraint 'cli-ban-dummy-on-node1' with a score
=#=#=#= Begin test: Remove expired constraints =#=#=#=
Removing constraint: cli-ban-dummy-on-node1
=#=#=#= Current cib after: Remove expired constraints =#=#=#=
-<cib epoch="61" num_updates="0" admin_epoch="0">
+<cib epoch="63" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4400,7 +6718,7 @@ Removing constraint: cli-ban-dummy-on-node1
=#=#=#= Begin test: Clear all implicit constraints for dummy =#=#=#=
Removing constraint: cli-prefer-dummy
=#=#=#= Current cib after: Clear all implicit constraints for dummy =#=#=#=
-<cib epoch="62" num_updates="0" admin_epoch="0">
+<cib epoch="64" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4447,7 +6765,7 @@ Removing constraint: cli-prefer-dummy
* Passed: crm_resource - Clear all implicit constraints for dummy
=#=#=#= Begin test: Set a node health strategy =#=#=#=
=#=#=#= Current cib after: Set a node health strategy =#=#=#=
-<cib epoch="63" num_updates="0" admin_epoch="0">
+<cib epoch="65" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4495,7 +6813,7 @@ Removing constraint: cli-prefer-dummy
* Passed: crm_attribute - Set a node health strategy
=#=#=#= Begin test: Set a node health attribute =#=#=#=
=#=#=#= Current cib after: Set a node health attribute =#=#=#=
-<cib epoch="64" num_updates="0" admin_epoch="0">
+<cib epoch="66" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4556,7 +6874,7 @@ Removing constraint: cli-prefer-dummy
* Passed: crm_resource - Show why a resource is not running on an unhealthy node
=#=#=#= Begin test: Delete a resource =#=#=#=
=#=#=#= Current cib after: Delete a resource =#=#=#=
-<cib epoch="65" num_updates="0" admin_epoch="0">
+<cib epoch="67" num_updates="0" admin_epoch="0">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
@@ -4609,8 +6927,8 @@ Removing constraint: cli-prefer-dummy
<change operation="delete" path="/cib/configuration/comment" position="0"/>
<change operation="delete" path="/cib/configuration/comment" position="1"/>
<change operation="delete" path="/cib/configuration/resources/comment" position="0"/>
- <change operation="delete" path="/cib/configuration/resources/primitive[@id=&apos;Fencing&apos;]/operations/op[@id=&apos;Fencing-start-0&apos;]"/>
- <change operation="modify" path="/cib/configuration/crm_config/cluster_property_set[@id=&apos;cib-bootstrap-options&apos;]/nvpair[@id=&apos;cib-bootstrap-options-cluster-name&apos;]">
+ <change operation="delete" path="/cib/configuration/resources/primitive[@id='Fencing']/operations/op[@id='Fencing-start-0']"/>
+ <change operation="modify" path="/cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-cluster-name']">
<change-list>
<change-attr name="value" operation="set" value="mycluster"/>
<change-attr name="name" operation="set" value="cluster-name"/>
@@ -4628,7 +6946,7 @@ Removing constraint: cli-prefer-dummy
<change operation="create" path="/cib/configuration/resources" position="0">
<!-- test: modify this comment to say something different -->
</change>
- <change operation="modify" path="/cib/configuration/resources/primitive[@id=&apos;Fencing&apos;]/instance_attributes[@id=&apos;Fencing-params&apos;]/nvpair[@id=&apos;Fencing-pcmk_host_list&apos;]">
+ <change operation="modify" path="/cib/configuration/resources/primitive[@id='Fencing']/instance_attributes[@id='Fencing-params']/nvpair[@id='Fencing-pcmk_host_list']">
<change-list>
<change-attr name="value" operation="set" value="node1 node2 node3 node4"/>
</change-list>
@@ -4636,7 +6954,7 @@ Removing constraint: cli-prefer-dummy
<nvpair id="Fencing-pcmk_host_list" name="pcmk_host_list" value="node1 node2 node3 node4"/>
</change-result>
</change>
- <change operation="modify" path="/cib/configuration/resources/primitive[@id=&apos;Fencing&apos;]/operations/op[@id=&apos;Fencing-monitor-120s&apos;]">
+ <change operation="modify" path="/cib/configuration/resources/primitive[@id='Fencing']/operations/op[@id='Fencing-monitor-120s']">
<change-list>
<change-attr name="timeout" operation="set" value="120s"/>
<change-attr name="name" operation="set" value="monitor"/>
@@ -4645,9 +6963,9 @@ Removing constraint: cli-prefer-dummy
<op id="Fencing-monitor-120s" interval="120s" timeout="120s" name="monitor"/>
</change-result>
</change>
- <change operation="move" path="/cib/configuration/resources/primitive[@id=&apos;dummy&apos;]/instance_attributes[@id=&apos;dummy-params&apos;]/nvpair[@id=&apos;dummy-op_sleep&apos;]" position="1"/>
- <change operation="move" path="/cib/configuration/resources/primitive[@id=&apos;dummy&apos;]/instance_attributes[@id=&apos;dummy-params&apos;]/nvpair[@id=&apos;dummy-fake&apos;]" position="2"/>
- <change operation="modify" path="/cib/configuration/resources/primitive[@id=&apos;dummy&apos;]/operations/op[@id=&apos;dummy-monitor-5s&apos;]">
+ <change operation="move" path="/cib/configuration/resources/primitive[@id='dummy']/instance_attributes[@id='dummy-params']/nvpair[@id='dummy-op_sleep']" position="1"/>
+ <change operation="move" path="/cib/configuration/resources/primitive[@id='dummy']/instance_attributes[@id='dummy-params']/nvpair[@id='dummy-fake']" position="2"/>
+ <change operation="modify" path="/cib/configuration/resources/primitive[@id='dummy']/operations/op[@id='dummy-monitor-5s']">
<change-list>
<change-attr name="name" operation="set" value="monitor"/>
<change-attr name="timeout" operation="unset"/>
@@ -5225,11 +7543,36 @@ Resources colocated with gr2:
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Set a meta-attribute for primitive and resources colocated with it =#=#=#=
-Set 'prim5' option: id=prim5-meta_attributes-target-role set=prim5-meta_attributes name=target-role value=Stopped
-Set 'prim4' option: id=prim4-meta_attributes-target-role set=prim4-meta_attributes name=target-role value=Stopped
-Set 'prim10' option: id=prim10-meta_attributes-target-role set=prim10-meta_attributes name=target-role value=Stopped
-Set 'prim3' option: id=prim3-meta_attributes-target-role set=prim3-meta_attributes name=target-role value=Stopped
-Set 'prim2' option: id=prim2-meta_attributes-target-role set=prim2-meta_attributes name=target-role value=Stopped
+<pacemaker-result api-version="X" request="crm_resource -r prim5 --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml">
+ <resource-settings>
+ <primitive id="prim5">
+ <meta_attributes id="prim5-meta_attributes">
+ <nvpair id="prim5-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ <primitive id="prim4">
+ <meta_attributes id="prim4-meta_attributes">
+ <nvpair id="prim4-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ <primitive id="prim10">
+ <meta_attributes id="prim10-meta_attributes">
+ <nvpair id="prim10-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ <primitive id="prim3">
+ <meta_attributes id="prim3-meta_attributes">
+ <nvpair id="prim3-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ <primitive id="prim2">
+ <meta_attributes id="prim2-meta_attributes">
+ <nvpair id="prim2-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ </resource-settings>
+ <status code="0" message="OK"/>
+</pacemaker-result>
=#=#=#= End test: Set a meta-attribute for primitive and resources colocated with it - OK (0) =#=#=#=
* Passed: crm_resource - Set a meta-attribute for primitive and resources colocated with it
=#=#=#= Begin test: Set a meta-attribute for group and resource colocated with it =#=#=#=
@@ -5238,8 +7581,21 @@ Set 'prim7' option: id=prim7-meta_attributes-target-role set=prim7-meta_attribut
=#=#=#= End test: Set a meta-attribute for group and resource colocated with it - OK (0) =#=#=#=
* Passed: crm_resource - Set a meta-attribute for group and resource colocated with it
=#=#=#= Begin test: Set a meta-attribute for clone and resource colocated with it =#=#=#=
-Set 'clone' option: id=clone-meta_attributes-target-role set=clone-meta_attributes name=target-role value=Stopped
-Set 'prim9' option: id=prim9-meta_attributes-target-role set=prim9-meta_attributes name=target-role value=Stopped
+<pacemaker-result api-version="X" request="crm_resource -r clone --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml">
+ <resource-settings>
+ <clone id="clone">
+ <meta_attributes id="clone-meta_attributes">
+ <nvpair id="clone-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </clone>
+ <primitive id="prim9">
+ <meta_attributes id="prim9-meta_attributes">
+ <nvpair id="prim9-meta_attributes-target-role" value="Stopped" name="target-role"/>
+ </meta_attributes>
+ </primitive>
+ </resource-settings>
+ <status code="0" message="OK"/>
+</pacemaker-result>
=#=#=#= End test: Set a meta-attribute for clone and resource colocated with it - OK (0) =#=#=#=
* Passed: crm_resource - Set a meta-attribute for clone and resource colocated with it
=#=#=#= Begin test: Show resource digests =#=#=#=
@@ -5705,7 +8061,7 @@ Started: [ cluster01 cluster02 ]
Promoted: [ cluster02 ]
Unpromoted: [ cluster01 ]
-Only 'private' parameters to 60s-interval monitor for dummy on cluster02 changed: 0:0;16:2:0:4a9e64d6-e1dd-4395-917c-1596312eafe4
+Only 'private' parameters to 1m-interval monitor for dummy on cluster02 changed: 0:0;16:2:0:4a9e64d6-e1dd-4395-917c-1596312eafe4
Original: cluster01 capacity:
Original: cluster02 capacity:
Original: httpd-bundle-0 capacity:
@@ -5774,7 +8130,7 @@ Current cluster status:
Performing Requested Modifications:
* Injecting ping_monitor_10000@cluster02=1 into the configuration
- * Injecting attribute fail-count-ping#monitor_10000=value++ into /node_state '2'
+ * Injecting attribute fail-count-ping#monitor_10000=1 into /node_state '2'
* Injecting attribute last-failure-ping#monitor_10000= into /node_state '2'
Transition Summary:
@@ -6142,7 +8498,7 @@ crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Update a nonexistent promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Update a nonexistent promotable score attribute (XML)
=#=#=#= Begin test: Query after updating a nonexistent promotable score attribute =#=#=#=
-scope=status name=master-promotable-rsc value=1
+scope=status name=master-promotable-rsc value=1
=#=#=#= End test: Query after updating a nonexistent promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating a nonexistent promotable score attribute
=#=#=#= Begin test: Query after updating a nonexistent promotable score attribute (XML) =#=#=#=
@@ -6162,7 +8518,7 @@ scope=status name=master-promotable-rsc value=1
=#=#=#= End test: Update an existing promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Update an existing promotable score attribute (XML)
=#=#=#= Begin test: Query after updating an existing promotable score attribute =#=#=#=
-scope=status name=master-promotable-rsc value=5
+scope=status name=master-promotable-rsc value=5
=#=#=#= End test: Query after updating an existing promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating an existing promotable score attribute
=#=#=#= Begin test: Query after updating an existing promotable score attribute (XML) =#=#=#=
@@ -6206,7 +8562,7 @@ crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Update a promotable score attribute to -INFINITY (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Update a promotable score attribute to -INFINITY (XML)
=#=#=#= Begin test: Query after updating a promotable score attribute to -INFINITY =#=#=#=
-scope=status name=master-promotable-rsc value=-INFINITY
+scope=status name=master-promotable-rsc value=-INFINITY
=#=#=#= End test: Query after updating a promotable score attribute to -INFINITY - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating a promotable score attribute to -INFINITY
=#=#=#= Begin test: Query after updating a promotable score attribute to -INFINITY (XML) =#=#=#=
@@ -6217,7 +8573,7 @@ scope=status name=master-promotable-rsc value=-INFINITY
=#=#=#= End test: Query after updating a promotable score attribute to -INFINITY (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating a promotable score attribute to -INFINITY (XML)
=#=#=#= Begin test: Try OCF_RESOURCE_INSTANCE if -p is specified with an empty string =#=#=#=
-scope=status name=master-promotable-rsc value=-INFINITY
+scope=status name=master-promotable-rsc value=-INFINITY
=#=#=#= End test: Try OCF_RESOURCE_INSTANCE if -p is specified with an empty string - OK (0) =#=#=#=
* Passed: crm_attribute - Try OCF_RESOURCE_INSTANCE if -p is specified with an empty string
=#=#=#= Begin test: Return usage error if both -p and OCF_RESOURCE_INSTANCE are empty strings =#=#=#=
@@ -7048,7 +9404,7 @@ Diff: +++ 1.4.1 (null)
<cib epoch="4" num_updates="1" admin_epoch="1"/>
</change-result>
</change>
- <change operation="modify" path="/cib/configuration/resources/primitive[@id=&apos;dummy&apos;]">
+ <change operation="modify" path="/cib/configuration/resources/primitive[@id='dummy']">
<change-list>
<change-attr name="description" operation="set" value="desc"/>
</change-list>
@@ -7667,7 +10023,7 @@ Diff: +++ 0.1.0 (null)
-- /cib/status/node_state[@id='1']
-- /cib/status/node_state[@id='httpd-bundle-0']
-- /cib/status/node_state[@id='httpd-bundle-1']
-+ /cib: @crm_feature_set=3.19.0, @num_updates=0, @admin_epoch=0
++ /cib: @validate-with=pacemaker-X, @num_updates=0, @admin_epoch=0
-- /cib: @cib-last-written, @update-origin, @update-client, @update-user, @have-quorum, @dc-uuid
=#=#=#= End test: Get active shadow instance's diff (empty CIB) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (empty CIB)
@@ -7679,29 +10035,30 @@ Diff: +++ 0.1.0 (null)
<source admin_epoch="1" epoch="1" num_updates="173"/>
<target admin_epoch="0" epoch="1" num_updates="0"/>
</version>
- <change operation="delete" path="/cib/configuration/crm_config/cluster_property_set[@id=&apos;cib-bootstrap-options&apos;]"/>
- <change operation="delete" path="/cib/configuration/nodes/node[@id=&apos;1&apos;]"/>
- <change operation="delete" path="/cib/configuration/nodes/node[@id=&apos;2&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/clone[@id=&apos;ping-clone&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/primitive[@id=&apos;Fencing&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/primitive[@id=&apos;dummy&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/clone[@id=&apos;inactive-clone&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/group[@id=&apos;inactive-group&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/bundle[@id=&apos;httpd-bundle&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/group[@id=&apos;exim-group&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/clone[@id=&apos;mysql-clone-group&apos;]"/>
- <change operation="delete" path="/cib/configuration/resources/clone[@id=&apos;promotable-clone&apos;]"/>
- <change operation="delete" path="/cib/configuration/constraints/rsc_location[@id=&apos;not-on-cluster1&apos;]"/>
- <change operation="delete" path="/cib/configuration/constraints/rsc_location[@id=&apos;loc-promotable-clone&apos;]"/>
+ <change operation="delete" path="/cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']"/>
+ <change operation="delete" path="/cib/configuration/nodes/node[@id='1']"/>
+ <change operation="delete" path="/cib/configuration/nodes/node[@id='2']"/>
+ <change operation="delete" path="/cib/configuration/resources/clone[@id='ping-clone']"/>
+ <change operation="delete" path="/cib/configuration/resources/primitive[@id='Fencing']"/>
+ <change operation="delete" path="/cib/configuration/resources/primitive[@id='dummy']"/>
+ <change operation="delete" path="/cib/configuration/resources/clone[@id='inactive-clone']"/>
+ <change operation="delete" path="/cib/configuration/resources/group[@id='inactive-group']"/>
+ <change operation="delete" path="/cib/configuration/resources/bundle[@id='httpd-bundle']"/>
+ <change operation="delete" path="/cib/configuration/resources/group[@id='exim-group']"/>
+ <change operation="delete" path="/cib/configuration/resources/clone[@id='mysql-clone-group']"/>
+ <change operation="delete" path="/cib/configuration/resources/clone[@id='promotable-clone']"/>
+ <change operation="delete" path="/cib/configuration/constraints/rsc_location[@id='not-on-cluster1']"/>
+ <change operation="delete" path="/cib/configuration/constraints/rsc_location[@id='loc-promotable-clone']"/>
<change operation="delete" path="/cib/configuration/tags"/>
<change operation="delete" path="/cib/configuration/op_defaults"/>
- <change operation="delete" path="/cib/status/node_state[@id=&apos;2&apos;]"/>
- <change operation="delete" path="/cib/status/node_state[@id=&apos;1&apos;]"/>
- <change operation="delete" path="/cib/status/node_state[@id=&apos;httpd-bundle-0&apos;]"/>
- <change operation="delete" path="/cib/status/node_state[@id=&apos;httpd-bundle-1&apos;]"/>
+ <change operation="delete" path="/cib/status/node_state[@id='2']"/>
+ <change operation="delete" path="/cib/status/node_state[@id='1']"/>
+ <change operation="delete" path="/cib/status/node_state[@id='httpd-bundle-0']"/>
+ <change operation="delete" path="/cib/status/node_state[@id='httpd-bundle-1']"/>
<change operation="modify" path="/cib">
<change-list>
- <change-attr name="crm_feature_set" operation="set" value="3.19.0"/>
+ <change-attr name="crm_feature_set" operation="set" value=""/>
+ <change-attr name="validate-with" operation="set" value="pacemaker-X"/>
<change-attr name="num_updates" operation="set" value="0"/>
<change-attr name="admin_epoch" operation="set" value="0"/>
<change-attr name="cib-last-written" operation="unset"/>
@@ -7898,49 +10255,63 @@ crm_shadow: Could not access shadow instance 'cts-cli': No such file or director
</pacemaker-result>
=#=#=#= End test: Switch to nonexistent shadow instance (force) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Switch to nonexistent shadow instance (force) (XML)
-=#=#=#= Begin test: Verify a file-specified invalid configuration, outputting as xml =#=#=#=
+=#=#=#= Begin test: Verify a file-specified invalid configuration (text output) =#=#=#=
+Errors found during check: config not valid
+-V may provide more details
+=#=#=#= End test: Verify a file-specified invalid configuration (text output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (text output)
+=#=#=#= Begin test: Verify a file-specified invalid configuration (verbose text output) =#=#=#=
+unpack_config warning: Blind faith: not fencing unseen nodes
+Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resource
+Ignoring <clone> resource 'test2-clone' because configuration is invalid
+CIB did not pass schema validation
+Errors found during check: config not valid
+=#=#=#= End test: Verify a file-specified invalid configuration (verbose text output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (verbose text output)
+=#=#=#= Begin test: Verify a file-specified invalid configuration (quiet text output) =#=#=#=
+=#=#=#= End test: Verify a file-specified invalid configuration (quiet text output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (quiet text output)
+=#=#=#= Begin test: Verify a file-specified invalid configuration (XML output) =#=#=#=
<pacemaker-result api-version="X" request="crm_verify_invalid_bz.xml --output-as=xml">
<status code="78" message="Invalid configuration">
<errors>
<error>Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resource</error>
<error>Ignoring &lt;clone&gt; resource 'test2-clone' because configuration is invalid</error>
- <error>crm_verify: Errors found during check: config not valid</error>
+ <error>CIB did not pass schema validation</error>
+ <error>Errors found during check: config not valid</error>
</errors>
</status>
</pacemaker-result>
-=#=#=#= End test: Verify a file-specified invalid configuration, outputting as xml - Invalid configuration (78) =#=#=#=
-* Passed: crm_verify - Verify a file-specified invalid configuration, outputting as xml
-=#=#=#= Begin test: Verify another file-specified invalid configuration, outputting as xml =#=#=#=
-<pacemaker-result api-version="X" request="crm_verify_invalid_no_stonith.xml --output-as=xml">
+=#=#=#= End test: Verify a file-specified invalid configuration (XML output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (XML output)
+=#=#=#= Begin test: Verify a file-specified invalid configuration (verbose XML output) =#=#=#=
+unpack_config warning: Blind faith: not fencing unseen nodes
+<pacemaker-result api-version="X" request="crm_verify_invalid_bz.xml --output-as=xml --verbose">
<status code="78" message="Invalid configuration">
<errors>
- <error>Resource start-up disabled since no STONITH resources have been defined</error>
- <error>Either configure some or disable STONITH with the stonith-enabled option</error>
- <error>NOTE: Clusters with shared data need STONITH to ensure data integrity</error>
- <error>Node pcmk-1 is unclean but cannot be fenced</error>
- <error>Node pcmk-2 is unclean but cannot be fenced</error>
- <error>crm_verify: Errors found during check: config not valid</error>
+ <error>Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resource</error>
+ <error>Ignoring &lt;clone&gt; resource 'test2-clone' because configuration is invalid</error>
+ <error>CIB did not pass schema validation</error>
+ <error>Errors found during check: config not valid</error>
</errors>
</status>
</pacemaker-result>
-=#=#=#= End test: Verify another file-specified invalid configuration, outputting as xml - Invalid configuration (78) =#=#=#=
-* Passed: crm_verify - Verify another file-specified invalid configuration, outputting as xml
-=#=#=#= Begin test: Verbosely verify a file-specified invalid configuration, outputting as xml =#=#=#=
-unpack_config warning: Blind faith: not fencing unseen nodes
-<pacemaker-result api-version="X" request="crm_verify_invalid_bz.xml --output-as=xml --verbose">
+=#=#=#= End test: Verify a file-specified invalid configuration (verbose XML output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (verbose XML output)
+=#=#=#= Begin test: Verify a file-specified invalid configuration (quiet XML output) =#=#=#=
+<pacemaker-result api-version="X" request="crm_verify_invalid_bz.xml --output-as=xml --quiet">
<status code="78" message="Invalid configuration">
<errors>
<error>Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resource</error>
<error>Ignoring &lt;clone&gt; resource 'test2-clone' because configuration is invalid</error>
- <error>crm_verify: Errors found during check: config not valid</error>
+ <error>CIB did not pass schema validation</error>
</errors>
</status>
</pacemaker-result>
-=#=#=#= End test: Verbosely verify a file-specified invalid configuration, outputting as xml - Invalid configuration (78) =#=#=#=
-* Passed: crm_verify - Verbosely verify a file-specified invalid configuration, outputting as xml
-=#=#=#= Begin test: Verbosely verify another file-specified invalid configuration, outputting as xml =#=#=#=
-(cluster_status@status.c:113) warning: Fencing and resource management disabled due to lack of quorum
-<pacemaker-result api-version="X" request="crm_verify_invalid_no_stonith.xml --output-as=xml --verbose">
+=#=#=#= End test: Verify a file-specified invalid configuration (quiet XML output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify a file-specified invalid configuration (quiet XML output)
+=#=#=#= Begin test: Verify another file-specified invalid configuration (XML output) =#=#=#=
+<pacemaker-result api-version="X" request="crm_verify_invalid_no_stonith.xml --output-as=xml">
<status code="78" message="Invalid configuration">
<errors>
<error>Resource start-up disabled since no STONITH resources have been defined</error>
@@ -7948,12 +10319,13 @@ unpack_config warning: Blind faith: not fencing unseen nodes
<error>NOTE: Clusters with shared data need STONITH to ensure data integrity</error>
<error>Node pcmk-1 is unclean but cannot be fenced</error>
<error>Node pcmk-2 is unclean but cannot be fenced</error>
- <error>crm_verify: Errors found during check: config not valid</error>
+ <error>CIB did not pass schema validation</error>
+ <error>Errors found during check: config not valid</error>
</errors>
</status>
</pacemaker-result>
-=#=#=#= End test: Verbosely verify another file-specified invalid configuration, outputting as xml - Invalid configuration (78) =#=#=#=
-* Passed: crm_verify - Verbosely verify another file-specified invalid configuration, outputting as xml
+=#=#=#= End test: Verify another file-specified invalid configuration (XML output) - Invalid configuration (78) =#=#=#=
+* Passed: crm_verify - Verify another file-specified invalid configuration (XML output)
=#=#=#= Begin test: Verify a file-specified valid configuration, outputting as xml =#=#=#=
<pacemaker-result api-version="X" request="crm_mon.xml --output-as=xml">
<status code="0" message="OK"/>
diff --git a/cts/cli/regression.upgrade.exp b/cts/cli/regression.upgrade.exp
index d1aeeb5..00ee754 100644
--- a/cts/cli/regression.upgrade.exp
+++ b/cts/cli/regression.upgrade.exp
@@ -57,47 +57,28 @@ A new shadow instance was created. To begin using it, enter the following into y
=#=#=#= End test: Configure the initial resource - OK (0) =#=#=#=
* Passed: cibadmin - Configure the initial resource
=#=#=#= Begin test: Upgrade to latest CIB schema (trigger 2.10.xsl + the wrapping) =#=#=#=
-update_validation debug: Testing 'pacemaker-2.10' validation (13 of X)
-update_validation debug: Upgrading pacemaker-2.10-style configuration to pacemaker-3.0 with upgrade-2.10.xsl
-apply_upgrade debug: Upgrading pacemaker-2.10-style configuration, pre-upgrade phase with upgrade-2.10-enter.xsl
-apply_upgrade debug: Upgrading pacemaker-2.10-style configuration, main phase with upgrade-2.10.xsl
+pcmk__update_schema debug: Schema pacemaker-2.10 validates
+apply_upgrade debug: Upgrading schema from pacemaker-2.10 to pacemaker-3.0: applying pre-upgrade XSL transform upgrade-2.10-enter
+apply_upgrade debug: Upgrading schema from pacemaker-2.10 to pacemaker-3.0: applying upgrade XSL transform upgrade-2.10
INFO: Resources-operation instance_attributes: mySmartFuse-monitor-inputpower (rsc=mySmartFuse, meta=mySmartFuse-inputpower-instanceparams): dropping requires
INFO: Resources-operation instance_attributes: ... only start/promote operation taken into account
INFO: Resources-operation instance_attributes: mySmartFuse-monitor-outputpower (rsc=mySmartFuse, meta=mySmartFuse-outputpower-instanceparams): dropping requires
INFO: Resources-operation instance_attributes: ... only start/promote operation taken into account
-apply_upgrade debug: Upgrading pacemaker-2.10-style configuration, post-upgrade phase with upgrade-2.10-leave.xsl
+apply_upgrade debug: Upgrading schema from pacemaker-2.10 to pacemaker-3.0: applying post-upgrade XSL transform upgrade-2.10-leave
DEBUG: instance_attributes: original element pointed to with @id-ref (mySmartFuse-outputpower-instanceparams) disappeared during upgrade
-update_validation info: Transformation upgrade-2.10.xsl successful
-update_validation debug: Testing 'pacemaker-3.0' validation (14 of X)
-update_validation debug: pacemaker-3.0-style configuration is also valid for pacemaker-3.1
-update_validation debug: Testing 'pacemaker-3.1' validation (15 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.1
-update_validation debug: pacemaker-3.1-style configuration is also valid for pacemaker-3.2
-update_validation debug: Testing 'pacemaker-3.2' validation (16 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.2
-update_validation debug: pacemaker-3.2-style configuration is also valid for pacemaker-3.3
-update_validation debug: Testing 'pacemaker-3.3' validation (17 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.3
-update_validation debug: pacemaker-3.3-style configuration is also valid for pacemaker-3.4
-update_validation debug: Testing 'pacemaker-3.4' validation (18 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.4
-update_validation debug: pacemaker-3.4-style configuration is also valid for pacemaker-3.5
-update_validation debug: Testing 'pacemaker-3.5' validation (19 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.5
-update_validation debug: pacemaker-3.5-style configuration is also valid for pacemaker-3.6
-update_validation debug: Testing 'pacemaker-3.6' validation (20 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.6
-update_validation debug: pacemaker-3.6-style configuration is also valid for pacemaker-3.7
-update_validation debug: Testing 'pacemaker-3.7' validation (21 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.7
-update_validation debug: pacemaker-3.7-style configuration is also valid for pacemaker-3.8
-update_validation debug: Testing 'pacemaker-3.8' validation (22 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.8
-update_validation debug: pacemaker-3.8-style configuration is also valid for pacemaker-3.9
-update_validation debug: Testing 'pacemaker-3.9' validation (23 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.9
-update_validation trace: Stopping at pacemaker-3.9
-update_validation info: Transformed the configuration from pacemaker-2.10 to pacemaker-3.9
+apply_upgrade info: Schema upgrade from pacemaker-2.10 to pacemaker-3.0 succeeded
+pcmk__update_schema debug: Schema pacemaker-3.0 validates
+pcmk__update_schema debug: Schema pacemaker-3.1 validates
+pcmk__update_schema debug: Schema pacemaker-3.2 validates
+pcmk__update_schema debug: Schema pacemaker-3.3 validates
+pcmk__update_schema debug: Schema pacemaker-3.4 validates
+pcmk__update_schema debug: Schema pacemaker-3.5 validates
+pcmk__update_schema debug: Schema pacemaker-3.6 validates
+pcmk__update_schema debug: Schema pacemaker-3.7 validates
+pcmk__update_schema debug: Schema pacemaker-3.8 validates
+pcmk__update_schema debug: Schema pacemaker-3.9 validates
+pcmk__update_schema debug: Schema pacemaker-3.10 validates
+pcmk__update_schema info: Transformed the configuration schema to pacemaker-3.10
=#=#=#= Current cib after: Upgrade to latest CIB schema (trigger 2.10.xsl + the wrapping) =#=#=#=
<cib epoch="2" num_updates="0" admin_epoch="1">
<configuration>
diff --git a/cts/cli/regression.validity.exp b/cts/cli/regression.validity.exp
index 3a62ab4..f020b20 100644
--- a/cts/cli/regression.validity.exp
+++ b/cts/cli/regression.validity.exp
@@ -2,21 +2,6 @@ Created new pacemaker configuration
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Try to make resulting CIB invalid (enum violation) =#=#=#=
- 1 <cib epoch="5" num_updates="0" admin_epoch="0">
- 2 <configuration>
- 3 <crm_config/>
- 4 <nodes/>
- 5 <resources>
- 6 <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
- 7 <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
- 8 </resources>
- 9 <constraints>
- 10 <rsc_order id="ord_1-2" first="dummy1" first-action="break" then="dummy2"/>
- 11 </constraints>
- 12 </configuration>
- 13 <status/>
- 14 </cib>
- 15
Call failed: Update does not conform to the configured schema
=#=#=#= Current cib after: Try to make resulting CIB invalid (enum violation) =#=#=#=
<cib epoch="4" num_updates="0" admin_epoch="0">
@@ -36,117 +21,82 @@ Call failed: Update does not conform to the configured schema
=#=#=#= End test: Try to make resulting CIB invalid (enum violation) - Invalid configuration (78) =#=#=#=
* Passed: cibadmin - Try to make resulting CIB invalid (enum violation)
=#=#=#= Begin test: Run crm_simulate with invalid CIB (enum violation) =#=#=#=
-update_validation debug: Testing 'pacemaker-1.2' validation (1 of X)
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-1.2 validation failed
-update_validation debug: Testing 'pacemaker-1.3' validation (2 of X)
+pcmk__update_schema debug: Schema pacemaker-1.2 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-1.3 validation failed
-update_validation debug: Testing 'pacemaker-2.0' validation (3 of X)
+pcmk__update_schema debug: Schema pacemaker-1.3 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.0 validation failed
-update_validation debug: Testing 'pacemaker-2.1' validation (4 of X)
+pcmk__update_schema debug: Schema pacemaker-2.0 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.1 validation failed
-update_validation debug: Testing 'pacemaker-2.2' validation (5 of X)
+pcmk__update_schema debug: Schema pacemaker-2.1 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.2 validation failed
-update_validation debug: Testing 'pacemaker-2.3' validation (6 of X)
+pcmk__update_schema debug: Schema pacemaker-2.2 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.3 validation failed
-update_validation debug: Testing 'pacemaker-2.4' validation (7 of X)
+pcmk__update_schema debug: Schema pacemaker-2.3 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.4 validation failed
-update_validation debug: Testing 'pacemaker-2.5' validation (8 of X)
+pcmk__update_schema debug: Schema pacemaker-2.4 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.5 validation failed
-update_validation debug: Testing 'pacemaker-2.6' validation (9 of X)
+pcmk__update_schema debug: Schema pacemaker-2.5 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.6 validation failed
-update_validation debug: Testing 'pacemaker-2.7' validation (10 of X)
+pcmk__update_schema debug: Schema pacemaker-2.6 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.7 validation failed
-update_validation debug: Testing 'pacemaker-2.8' validation (11 of X)
+pcmk__update_schema debug: Schema pacemaker-2.7 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.8 validation failed
-update_validation debug: Testing 'pacemaker-2.9' validation (12 of X)
+pcmk__update_schema debug: Schema pacemaker-2.8 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.9 validation failed
-update_validation debug: Testing 'pacemaker-2.10' validation (13 of X)
+pcmk__update_schema debug: Schema pacemaker-2.9 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-2.10 validation failed
-update_validation debug: Testing 'pacemaker-3.0' validation (14 of X)
+pcmk__update_schema debug: Schema pacemaker-2.10 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.0 validation failed
-update_validation debug: Testing 'pacemaker-3.1' validation (15 of X)
+pcmk__update_schema debug: Schema pacemaker-3.0 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.1 validation failed
-update_validation debug: Testing 'pacemaker-3.2' validation (16 of X)
+pcmk__update_schema debug: Schema pacemaker-3.1 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.2 validation failed
-update_validation debug: Testing 'pacemaker-3.3' validation (17 of X)
+pcmk__update_schema debug: Schema pacemaker-3.2 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.3 validation failed
-update_validation debug: Testing 'pacemaker-3.4' validation (18 of X)
+pcmk__update_schema debug: Schema pacemaker-3.3 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.4 validation failed
-update_validation debug: Testing 'pacemaker-3.5' validation (19 of X)
+pcmk__update_schema debug: Schema pacemaker-3.4 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.5 validation failed
-update_validation debug: Testing 'pacemaker-3.6' validation (20 of X)
+pcmk__update_schema debug: Schema pacemaker-3.5 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.6 validation failed
-update_validation debug: Testing 'pacemaker-3.7' validation (21 of X)
+pcmk__update_schema debug: Schema pacemaker-3.6 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.7 validation failed
-update_validation debug: Testing 'pacemaker-3.8' validation (22 of X)
+pcmk__update_schema debug: Schema pacemaker-3.7 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.8 validation failed
-update_validation debug: Testing 'pacemaker-3.9' validation (23 of X)
+pcmk__update_schema debug: Schema pacemaker-3.8 does not validate
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
-update_validation trace: pacemaker-3.9 validation failed
-Cannot upgrade configuration (claiming schema pacemaker-1.2) to at least pacemaker-3.0 because it does not validate with any schema from pacemaker-1.2 to pacemaker-3.9
+pcmk__update_schema debug: Schema pacemaker-3.9 does not validate
+element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
+element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
+pcmk__update_schema debug: Schema pacemaker-3.10 does not validate
+Cannot upgrade configuration (claiming pacemaker-1.2 schema) to at least pacemaker-3.0 because it does not validate with any schema from pacemaker-1.2 to the latest
=#=#=#= End test: Run crm_simulate with invalid CIB (enum violation) - Invalid configuration (78) =#=#=#=
* Passed: crm_simulate - Run crm_simulate with invalid CIB (enum violation)
=#=#=#= Begin test: Try to make resulting CIB invalid (unrecognized validate-with) =#=#=#=
- 1 <cib epoch="4" num_updates="1" admin_epoch="0">
- 2 <configuration>
- 3 <crm_config/>
- 4 <nodes/>
- 5 <resources>
- 6 <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
- 7 <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
- 8 </resources>
- 9 <constraints>
- 10 <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
- 11 </constraints>
- 12 </configuration>
- 13 <status/>
- 14 </cib>
- 15
Call failed: Update does not conform to the configured schema
=#=#=#= Current cib after: Try to make resulting CIB invalid (unrecognized validate-with) =#=#=#=
<cib epoch="4" num_updates="0" admin_epoch="0">
@@ -166,99 +116,60 @@ Call failed: Update does not conform to the configured schema
=#=#=#= End test: Try to make resulting CIB invalid (unrecognized validate-with) - Invalid configuration (78) =#=#=#=
* Passed: cibadmin - Try to make resulting CIB invalid (unrecognized validate-with)
=#=#=#= Begin test: Run crm_simulate with invalid CIB (unrecognized validate-with) =#=#=#=
-update_validation debug: Unknown validation schema
-update_validation debug: Testing 'pacemaker-1.0' validation (0 of X)
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-1.0 validation failed
-update_validation debug: Testing 'pacemaker-1.2' validation (1 of X)
+pcmk__update_schema debug: Schema pacemaker-1.0 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-1.2 validation failed
-update_validation debug: Testing 'pacemaker-1.3' validation (2 of X)
+pcmk__update_schema debug: Schema pacemaker-1.2 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-1.3 validation failed
-update_validation debug: Testing 'pacemaker-2.0' validation (3 of X)
+pcmk__update_schema debug: Schema pacemaker-1.3 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.0 validation failed
-update_validation debug: Testing 'pacemaker-2.1' validation (4 of X)
+pcmk__update_schema debug: Schema pacemaker-2.0 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.1 validation failed
-update_validation debug: Testing 'pacemaker-2.2' validation (5 of X)
+pcmk__update_schema debug: Schema pacemaker-2.1 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.2 validation failed
-update_validation debug: Testing 'pacemaker-2.3' validation (6 of X)
+pcmk__update_schema debug: Schema pacemaker-2.2 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.3 validation failed
-update_validation debug: Testing 'pacemaker-2.4' validation (7 of X)
+pcmk__update_schema debug: Schema pacemaker-2.3 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.4 validation failed
-update_validation debug: Testing 'pacemaker-2.5' validation (8 of X)
+pcmk__update_schema debug: Schema pacemaker-2.4 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.5 validation failed
-update_validation debug: Testing 'pacemaker-2.6' validation (9 of X)
+pcmk__update_schema debug: Schema pacemaker-2.5 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.6 validation failed
-update_validation debug: Testing 'pacemaker-2.7' validation (10 of X)
+pcmk__update_schema debug: Schema pacemaker-2.6 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.7 validation failed
-update_validation debug: Testing 'pacemaker-2.8' validation (11 of X)
+pcmk__update_schema debug: Schema pacemaker-2.7 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.8 validation failed
-update_validation debug: Testing 'pacemaker-2.9' validation (12 of X)
+pcmk__update_schema debug: Schema pacemaker-2.8 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.9 validation failed
-update_validation debug: Testing 'pacemaker-2.10' validation (13 of X)
+pcmk__update_schema debug: Schema pacemaker-2.9 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-2.10 validation failed
-update_validation debug: Testing 'pacemaker-3.0' validation (14 of X)
+pcmk__update_schema debug: Schema pacemaker-2.10 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.0 validation failed
-update_validation debug: Testing 'pacemaker-3.1' validation (15 of X)
+pcmk__update_schema debug: Schema pacemaker-3.0 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.1 validation failed
-update_validation debug: Testing 'pacemaker-3.2' validation (16 of X)
+pcmk__update_schema debug: Schema pacemaker-3.1 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.2 validation failed
-update_validation debug: Testing 'pacemaker-3.3' validation (17 of X)
+pcmk__update_schema debug: Schema pacemaker-3.2 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.3 validation failed
-update_validation debug: Testing 'pacemaker-3.4' validation (18 of X)
+pcmk__update_schema debug: Schema pacemaker-3.3 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.4 validation failed
-update_validation debug: Testing 'pacemaker-3.5' validation (19 of X)
+pcmk__update_schema debug: Schema pacemaker-3.4 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.5 validation failed
-update_validation debug: Testing 'pacemaker-3.6' validation (20 of X)
+pcmk__update_schema debug: Schema pacemaker-3.5 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.6 validation failed
-update_validation debug: Testing 'pacemaker-3.7' validation (21 of X)
+pcmk__update_schema debug: Schema pacemaker-3.6 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.7 validation failed
-update_validation debug: Testing 'pacemaker-3.8' validation (22 of X)
+pcmk__update_schema debug: Schema pacemaker-3.7 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.8 validation failed
-update_validation debug: Testing 'pacemaker-3.9' validation (23 of X)
+pcmk__update_schema debug: Schema pacemaker-3.8 does not validate
element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
-update_validation trace: pacemaker-3.9 validation failed
-Cannot upgrade configuration (claiming schema pacemaker-9999.0) to at least pacemaker-3.0 because it does not validate with any schema from unknown to pacemaker-3.9
+pcmk__update_schema debug: Schema pacemaker-3.9 does not validate
+element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
+pcmk__update_schema debug: Schema pacemaker-3.10 does not validate
+Cannot upgrade configuration (claiming pacemaker-9999.0 schema) to at least pacemaker-3.0 because it does not validate with any schema from the first to the latest
=#=#=#= End test: Run crm_simulate with invalid CIB (unrecognized validate-with) - Invalid configuration (78) =#=#=#=
* Passed: crm_simulate - Run crm_simulate with invalid CIB (unrecognized validate-with)
=#=#=#= Begin test: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) =#=#=#=
- 1 <cib epoch="4" num_updates="0" admin_epoch="0">
- 2 <configuration>
- 3 <crm_config/>
- 4 <nodes/>
- 5 <resources>
- 6 <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
- 7 <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
- 8 </resources>
- 9 <constraints>
- 10 <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
- 11 </constraints>
- 12 <tags/>
- 13 </configuration>
- 14 <status/>
- 15 </cib>
- 16
Call failed: Update does not conform to the configured schema
=#=#=#= Current cib after: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) =#=#=#=
<cib epoch="4" num_updates="0" admin_epoch="0">
@@ -278,75 +189,32 @@ Call failed: Update does not conform to the configured schema
=#=#=#= End test: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) - Invalid configuration (78) =#=#=#=
* Passed: cibadmin - Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1)
=#=#=#= Begin test: Run crm_simulate with invalid, but possibly recoverable CIB (valid with X.Y+1) =#=#=#=
-update_validation debug: Testing 'pacemaker-1.2' validation (1 of X)
element tags: Relax-NG validity error : Element configuration has extra content: tags
-update_validation trace: pacemaker-1.2 validation failed
-update_validation debug: Testing 'pacemaker-1.3' validation (2 of X)
-update_validation debug: pacemaker-1.3-style configuration is also valid for pacemaker-2.0
-update_validation debug: Testing 'pacemaker-2.0' validation (3 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.0
-update_validation debug: pacemaker-2.0-style configuration is also valid for pacemaker-2.1
-update_validation debug: Testing 'pacemaker-2.1' validation (4 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.1
-update_validation debug: pacemaker-2.1-style configuration is also valid for pacemaker-2.2
-update_validation debug: Testing 'pacemaker-2.2' validation (5 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.2
-update_validation debug: pacemaker-2.2-style configuration is also valid for pacemaker-2.3
-update_validation debug: Testing 'pacemaker-2.3' validation (6 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.3
-update_validation debug: pacemaker-2.3-style configuration is also valid for pacemaker-2.4
-update_validation debug: Testing 'pacemaker-2.4' validation (7 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.4
-update_validation debug: pacemaker-2.4-style configuration is also valid for pacemaker-2.5
-update_validation debug: Testing 'pacemaker-2.5' validation (8 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.5
-update_validation debug: pacemaker-2.5-style configuration is also valid for pacemaker-2.6
-update_validation debug: Testing 'pacemaker-2.6' validation (9 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.6
-update_validation debug: pacemaker-2.6-style configuration is also valid for pacemaker-2.7
-update_validation debug: Testing 'pacemaker-2.7' validation (10 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.7
-update_validation debug: pacemaker-2.7-style configuration is also valid for pacemaker-2.8
-update_validation debug: Testing 'pacemaker-2.8' validation (11 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.8
-update_validation debug: pacemaker-2.8-style configuration is also valid for pacemaker-2.9
-update_validation debug: Testing 'pacemaker-2.9' validation (12 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.9
-update_validation debug: pacemaker-2.9-style configuration is also valid for pacemaker-2.10
-update_validation debug: Testing 'pacemaker-2.10' validation (13 of X)
-update_validation debug: Configuration valid for schema: pacemaker-2.10
-update_validation debug: pacemaker-2.10-style configuration is also valid for pacemaker-3.0
-update_validation debug: Testing 'pacemaker-3.0' validation (14 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.0
-update_validation debug: pacemaker-3.0-style configuration is also valid for pacemaker-3.1
-update_validation debug: Testing 'pacemaker-3.1' validation (15 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.1
-update_validation debug: pacemaker-3.1-style configuration is also valid for pacemaker-3.2
-update_validation debug: Testing 'pacemaker-3.2' validation (16 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.2
-update_validation debug: pacemaker-3.2-style configuration is also valid for pacemaker-3.3
-update_validation debug: Testing 'pacemaker-3.3' validation (17 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.3
-update_validation debug: pacemaker-3.3-style configuration is also valid for pacemaker-3.4
-update_validation debug: Testing 'pacemaker-3.4' validation (18 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.4
-update_validation debug: pacemaker-3.4-style configuration is also valid for pacemaker-3.5
-update_validation debug: Testing 'pacemaker-3.5' validation (19 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.5
-update_validation debug: pacemaker-3.5-style configuration is also valid for pacemaker-3.6
-update_validation debug: Testing 'pacemaker-3.6' validation (20 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.6
-update_validation debug: pacemaker-3.6-style configuration is also valid for pacemaker-3.7
-update_validation debug: Testing 'pacemaker-3.7' validation (21 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.7
-update_validation debug: pacemaker-3.7-style configuration is also valid for pacemaker-3.8
-update_validation debug: Testing 'pacemaker-3.8' validation (22 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.8
-update_validation debug: pacemaker-3.8-style configuration is also valid for pacemaker-3.9
-update_validation debug: Testing 'pacemaker-3.9' validation (23 of X)
-update_validation debug: Configuration valid for schema: pacemaker-3.9
-update_validation trace: Stopping at pacemaker-3.9
-update_validation info: Transformed the configuration from pacemaker-1.2 to pacemaker-3.9
+pcmk__update_schema debug: Schema pacemaker-1.2 does not validate
+pcmk__update_schema debug: Schema pacemaker-1.3 validates
+pcmk__update_schema debug: Schema pacemaker-2.0 validates
+pcmk__update_schema debug: Schema pacemaker-2.1 validates
+pcmk__update_schema debug: Schema pacemaker-2.2 validates
+pcmk__update_schema debug: Schema pacemaker-2.3 validates
+pcmk__update_schema debug: Schema pacemaker-2.4 validates
+pcmk__update_schema debug: Schema pacemaker-2.5 validates
+pcmk__update_schema debug: Schema pacemaker-2.6 validates
+pcmk__update_schema debug: Schema pacemaker-2.7 validates
+pcmk__update_schema debug: Schema pacemaker-2.8 validates
+pcmk__update_schema debug: Schema pacemaker-2.9 validates
+pcmk__update_schema debug: Schema pacemaker-2.10 validates
+pcmk__update_schema debug: Schema pacemaker-3.0 validates
+pcmk__update_schema debug: Schema pacemaker-3.1 validates
+pcmk__update_schema debug: Schema pacemaker-3.2 validates
+pcmk__update_schema debug: Schema pacemaker-3.3 validates
+pcmk__update_schema debug: Schema pacemaker-3.4 validates
+pcmk__update_schema debug: Schema pacemaker-3.5 validates
+pcmk__update_schema debug: Schema pacemaker-3.6 validates
+pcmk__update_schema debug: Schema pacemaker-3.7 validates
+pcmk__update_schema debug: Schema pacemaker-3.8 validates
+pcmk__update_schema debug: Schema pacemaker-3.9 validates
+pcmk__update_schema debug: Schema pacemaker-3.10 validates
+pcmk__update_schema info: Transformed the configuration schema to pacemaker-3.10
unpack_resources error: Resource start-up disabled since no STONITH resources have been defined
unpack_resources error: Either configure some or disable STONITH with the stonith-enabled option
unpack_resources error: NOTE: Clusters with shared data need STONITH to ensure data integrity
@@ -387,7 +255,7 @@ Revised Cluster Status:
=#=#=#= End test: Make resulting CIB valid, although without validate-with attribute - OK (0) =#=#=#=
* Passed: cibadmin - Make resulting CIB valid, although without validate-with attribute
=#=#=#= Begin test: Run crm_simulate with valid CIB, but without validate-with attribute =#=#=#=
-Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
+Schema validation of configuration is disabled (support for validate-with set to "none" is deprecated and will be removed in a future release)
unpack_resources error: Resource start-up disabled since no STONITH resources have been defined
unpack_resources error: Either configure some or disable STONITH with the stonith-enabled option
unpack_resources error: NOTE: Clusters with shared data need STONITH to ensure data integrity
@@ -460,6 +328,8 @@ element rsc_order: Relax-NG validity error : Invalid attribute first-action for
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
+element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
+element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
=#=#=#= Current cib after: Make resulting CIB invalid, and without validate-with attribute =#=#=#=
<cib epoch="41" num_updates="0" admin_epoch="0" validate-with="none">
<configuration>
@@ -478,7 +348,9 @@ element rsc_order: Relax-NG validity error : Element constraints has extra conte
=#=#=#= End test: Make resulting CIB invalid, and without validate-with attribute - OK (0) =#=#=#=
* Passed: cibadmin - Make resulting CIB invalid, and without validate-with attribute
=#=#=#= Begin test: Run crm_simulate with invalid CIB, also without validate-with attribute =#=#=#=
-Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
+Schema validation of configuration is disabled (support for validate-with set to "none" is deprecated and will be removed in a future release)
+validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
+validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
diff --git a/cts/cli/tickets.xml b/cts/cli/tickets.xml
new file mode 100644
index 0000000..7130abc
--- /dev/null
+++ b/cts/cli/tickets.xml
@@ -0,0 +1,32 @@
+<cib epoch="1" num_updates="1" admin_epoch="0" validate-with="pacemaker-3.9" cib-last-written="Tue Feb 27 11:16:17 2024" have-quorum="1">
+ <configuration>
+ <crm_config>
+ <cluster_property_set id="cib-bootstrap-options">
+ <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="false"/>
+ <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
+ </cluster_property_set>
+ </crm_config>
+ <nodes>
+ <node id="node1" type="member" uname="node1"/>
+ <node id="node2" type="member" uname="node2"/>
+ </nodes>
+ <resources>
+ <primitive class="ocf" id="rsc1" provider="pacemaker" type="Dummy"/>
+ <primitive class="ocf" id="rsc2" provider="pacemaker" type="Dummy"/>
+ </resources>
+ <constraints>
+ <rsc_ticket id="rsc1-req-ticketA" rsc="rsc1" ticket="ticketA" rsc-role="Started" loss-policy="stop"/>
+ <rsc_ticket id="rsc1-req-ticketB" rsc="rsc2" ticket="ticketB" rsc-role="Started" loss-policy="stop"/>
+ </constraints>
+ </configuration>
+ <status>
+ <node_state id="node1" uname="node1" ha="active" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate"/>
+ <node_state id="node2" uname="node2" ha="active" in_ccm="true" crmd="online" join="member" expected="member" crm-debug-origin="crm_simulate"/>
+ <tickets>
+ <ticket_state id="ticketA" status="revoked" granted="false" standby="false" owner="1"/>
+ <ticket_state id="ticketB" status="granted" granted="true" standby="false"/>
+ <ticket_state id="ticketC" status="granted" granted="true" standby="false"/>
+ <ticket_state id="ticketC" status="granted" granted="true" standby="false"/>
+ </tickets>
+ </status>
+</cib>
diff --git a/cts/cts-attrd.in b/cts/cts-attrd.in
index b594ac3..c9a219d 100644
--- a/cts/cts-attrd.in
+++ b/cts/cts-attrd.in
@@ -196,7 +196,7 @@ class AttributeTests(Tests):
test.add_cmd("attrd_updater", "--name DDD -U 555 --set=foo --output-as=xml")
test.add_cmd_check_stdout("attrd_updater", "--name DDD -Q --output-as=xml",
"name=\"DDD\" value=\"555\"")
- test.add_log_pattern("Processed 1 private change for DDD, id=n/a, set=foo")
+ test.add_log_pattern("Processed 1 private change for DDD (set foo)")
def build_multiple_query_tests(self):
""" Add tests that set and query an attribute across multiple nodes """
diff --git a/cts/cts-cli.in b/cts/cts-cli.in
index f4cb7c3..37dd530 100755
--- a/cts/cts-cli.in
+++ b/cts/cts-cli.in
@@ -1,6 +1,6 @@
#!@BASH_PATH@
#
-# 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.
#
@@ -23,8 +23,8 @@ Options:
--help Display this text, then exit
-V, --verbose Display any differences from expected output
-t 'TEST [...]' Run only specified tests
- (default: 'daemons dates error_codes tools crm_mon acls
- validity upgrade rules feature_set').
+ (default: 'access_render daemons dates error_codes tools
+ crm_mon acls validity upgrade rules feature_set').
Other tests: agents (must be run in an installed environment).
-p DIR Look for executables in DIR (may be specified multiple times)
-v, --valgrind Run all commands under valgrind
@@ -43,8 +43,8 @@ shadow_dir=$(mktemp -d ${TMPDIR:-/tmp}/cts-cli.shadow.XXXXXXXXXX)
num_errors=0
num_passed=0
verbose=0
-tests="daemons dates error_codes tools crm_mon acls validity upgrade rules "
-tests="$tests feature_set"
+tests="access_render daemons dates error_codes tools crm_mon acls validity"
+tests="$tests upgrade rules feature_set"
do_save=0
XMLLINT_CMD=
VALGRIND_CMD=
@@ -550,7 +550,7 @@ function test_crm_mon() {
desc="XML output of active unmanaged resource on offline node"
cmd="crm_mon -1 --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
desc="Brief text output of active unmanaged resource on offline node"
cmd="crm_mon -1 --brief"
@@ -812,12 +812,84 @@ function test_tools() {
cmd="cibadmin -Q"
test_assert $CRM_EX_OK
+ desc="List all available options (invalid type)"
+ cmd="crm_attribute --list-options=asdf"
+ test_assert $CRM_EX_USAGE 0
+
+ desc="List all available options (invalid type) (XML)"
+ cmd="crm_attribute --list-options=asdf --output-as=xml"
+ test_assert_validate $CRM_EX_USAGE 0
+
+ desc="List non-advanced cluster options"
+ cmd="crm_attribute --list-options=cluster"
+ test_assert $CRM_EX_OK 0
+
+ desc="List non-advanced cluster options (XML) (shows all)"
+ cmd="crm_attribute --list-options=cluster --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="List all available cluster options"
+ cmd="crm_attribute --list-options=cluster --all"
+ test_assert $CRM_EX_OK 0
+
+ desc="List all available cluster options (XML)"
+ cmd="crm_attribute --list-options=cluster --all --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="Query the value of an attribute that does not exist"
cmd="crm_attribute -n ABCD --query --quiet"
test_assert $CRM_EX_NOSUCH 0
desc="Configure something before erasing"
- cmd="crm_attribute -n cluster-delay -v 60s"
+ cmd="crm_attribute -n test_attr -v 5"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' XML attribute update syntax"
+ cmd="cibadmin -M --score --xml-text='<cib admin_epoch=\"admin_epoch++\"\>'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' XML attribute update syntax"
+ cmd="cibadmin -M --score --xml-text='<cib admin_epoch=\"admin_epoch+=2\"\>'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' nvpair value update syntax"
+ cmd="crm_attribute -n test_attr -v 'value++' --score"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' nvpair value update syntax (XML)"
+ cmd="crm_attribute -n test_attr -v 'value++' --score --output-as=xml"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' nvpair value update syntax"
+ cmd="crm_attribute -n test_attr -v 'value+=2' --score"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' nvpair value update syntax (XML)"
+ cmd="crm_attribute -n test_attr -v 'value+=2' --score --output-as=xml"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' XML attribute update syntax (--score not set)"
+ cmd="cibadmin -M --xml-text='<cib admin_epoch=\"admin_epoch++\"\>'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' XML attribute update syntax (--score not set)"
+ cmd="cibadmin -M --xml-text='<cib admin_epoch=\"admin_epoch+=2\"\>'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' nvpair value update syntax (--score not set)"
+ cmd="crm_attribute -n test_attr -v 'value++'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '++' nvpair value update syntax (--score not set) (XML)"
+ cmd="crm_attribute -n test_attr -v 'value++' --output-as=xml"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' nvpair value update syntax (--score not set)"
+ cmd="crm_attribute -n test_attr -v 'value+=2'"
+ test_assert $CRM_EX_OK
+
+ desc="Test '+=' nvpair value update syntax (--score not set) (XML)"
+ cmd="crm_attribute -n test_attr -v 'value+=2' --output-as=xml"
test_assert $CRM_EX_OK
desc="Require --force for CIB erasure"
@@ -989,6 +1061,46 @@ function test_tools() {
cmd="crm_resource foo bar"
test_assert $CRM_EX_USAGE 0
+ desc="List all available resource options (invalid type)"
+ cmd="crm_resource --list-options=asdf"
+ test_assert $CRM_EX_USAGE 0
+
+ desc="List all available resource options (invalid type) (XML)"
+ cmd="crm_resource --list-options=asdf --output-as=xml"
+ test_assert_validate $CRM_EX_USAGE 0
+
+ desc="List non-advanced primitive meta-attributes"
+ cmd="crm_resource --list-options=primitive"
+ test_assert $CRM_EX_OK 0
+
+ desc="List non-advanced primitive meta-attributes (XML) (shows all)"
+ cmd="crm_resource --list-options=primitive --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="List all available primitive meta-attributes"
+ cmd="crm_resource --list-options=primitive --all"
+ test_assert $CRM_EX_OK 0
+
+ desc="List all available primitive meta-attributes (XML)"
+ cmd="crm_resource --list-options=primitive --all --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="List non-advanced fencing parameters"
+ cmd="crm_resource --list-options=fencing"
+ test_assert $CRM_EX_OK 0
+
+ desc="List non-advanced fencing parameters (XML) (shows all)"
+ cmd="crm_resource --list-options=fencing --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="List all available fencing parameters"
+ cmd="crm_resource --list-options=fencing --all"
+ test_assert $CRM_EX_OK 0
+
+ desc="List all available fencing parameters (XML)"
+ cmd="crm_resource --list-options=fencing --all --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="crm_resource given both -r and resource config"
cmd="crm_resource -r xyz --class ocf --provider pacemaker --agent Dummy"
test_assert $CRM_EX_USAGE 0
@@ -1039,19 +1151,19 @@ function test_tools() {
desc="Set a non-existent attribute for a resource element with output-as=xml"
cmd="crm_resource -r dummy --set-parameter=description -v test_description --element --output-as=xml"
- test_assert $CRM_EX_OK
+ test_assert_validate $CRM_EX_OK
desc="Set an existent attribute for a resource element with output-as=xml"
cmd="crm_resource -r dummy --set-parameter=description -v test_description --element --output-as=xml"
- test_assert $CRM_EX_OK
+ test_assert_validate $CRM_EX_OK
desc="Delete an existent attribute for a resource element with output-as=xml"
cmd="crm_resource -r dummy -d description --element --output-as=xml"
- test_assert $CRM_EX_OK
+ test_assert_validate $CRM_EX_OK
desc="Delete a non-existent attribute for a resource element with output-as=xml"
cmd="crm_resource -r dummy -d description --element --output-as=xml"
- test_assert $CRM_EX_OK
+ test_assert_validate $CRM_EX_OK
desc="Set a non-existent attribute for a resource element without output-as=xml"
cmd="crm_resource -r dummy --set-parameter=description -v test_description --element"
@@ -1095,7 +1207,7 @@ function test_tools() {
desc="Show XML configuration of resource, output as XML"
cmd="crm_resource -q -r dummy --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
desc="Require a destination when migrating a resource that is stopped"
cmd="crm_resource -r dummy -M"
@@ -1137,10 +1249,30 @@ function test_tools() {
cmd="crm_ticket -t ticketA -r --force"
test_assert $CRM_EX_OK
+ desc="List ticket IDs"
+ cmd="crm_ticket -w"
+ test_assert $CRM_EX_OK 0
+
+ desc="List ticket IDs, outputting in XML"
+ cmd="crm_ticket -w --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="Query ticket state"
+ cmd="crm_ticket -t ticketA -q"
+ test_assert $CRM_EX_OK 0
+
+ desc="Query ticket state, outputting as xml"
+ cmd="crm_ticket -t ticketA -q --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="Query ticket granted state"
cmd="crm_ticket -t ticketA -G granted"
test_assert $CRM_EX_OK
+ desc="Query ticket granted state, outputting as xml"
+ cmd="crm_ticket -t ticketA -G granted --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
desc="Delete ticket granted state"
cmd="crm_ticket -t ticketA -D granted --force"
test_assert $CRM_EX_OK
@@ -1157,10 +1289,54 @@ function test_tools() {
cmd="crm_ticket -t ticketA -a"
test_assert $CRM_EX_OK
+ desc="List ticket details"
+ cmd="crm_ticket -L -t ticketA"
+ test_assert $CRM_EX_OK 0
+
+ desc="List ticket details, outputting as XML"
+ cmd="crm_ticket -L -t ticketA --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="Add a second ticket"
+ cmd="crm_ticket -t ticketB -G granted -d false"
+ test_assert $CRM_EX_OK
+
+ desc="Set second ticket granted state"
+ cmd="crm_ticket -t ticketB -r --force"
+ test_assert $CRM_EX_OK
+
+ desc="List tickets"
+ cmd="crm_ticket -l"
+ test_assert $CRM_EX_OK 0
+
+ desc="List tickets, outputting as XML"
+ cmd="crm_ticket -l --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="Delete second ticket"
+ cmd="cibadmin --delete --xml-text '<ticket_state id=\"ticketB\"/>'"
+ test_assert $CRM_EX_OK
+
desc="Delete ticket standby state"
cmd="crm_ticket -t ticketA -D standby"
test_assert $CRM_EX_OK
+ esc="Add a constraint to a ticket"
+ cmd="cibadmin -C -o constraints --xml-text '<rsc_ticket id=\"dummy-dep-ticketA\" rsc=\"dummy\" rsc-role=\"Started\" ticket=\"ticketA\" loss-policy=\"freeze\"/>'"
+ test_assert $CRM_EX_OK
+
+ desc="Query ticket constraints"
+ cmd="crm_ticket -t ticketA -c"
+ test_assert $CRM_EX_OK 0
+
+ desc="Query ticket constraints, outputting as xml"
+ cmd="crm_ticket -t ticketA -c --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
+
+ desc="Delete ticket constraint"
+ cmd="cibadmin --delete --xml-text '<rsc_ticket id=\"dummy-dep-ticketA\"/>'"
+ test_assert $CRM_EX_OK
+
desc="Ban a resource on unknown node"
cmd="crm_resource -r dummy -B -N host1"
test_assert $CRM_EX_NOSUCH
@@ -1363,16 +1539,16 @@ function test_tools() {
unset CIB_file
desc="Set a meta-attribute for primitive and resources colocated with it"
- cmd="crm_resource -r prim5 --meta --set-parameter=target-role -v Stopped --recursive"
- test_assert $CRM_EX_OK 0
+ cmd="crm_resource -r prim5 --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
desc="Set a meta-attribute for group and resource colocated with it"
cmd="crm_resource -r group --meta --set-parameter=target-role -v Stopped --recursive"
test_assert $CRM_EX_OK 0
desc="Set a meta-attribute for clone and resource colocated with it"
- cmd="crm_resource -r clone --meta --set-parameter=target-role -v Stopped --recursive"
- test_assert $CRM_EX_OK 0
+ cmd="crm_resource -r clone --meta --set-parameter=target-role -v Stopped --recursive --output-as=xml"
+ test_assert_validate $CRM_EX_OK 0
unset CIB_shadow
unset CIB_shadow_dir
@@ -1580,7 +1756,7 @@ function test_tools() {
desc="Update a promotable score attribute to -INFINITY (XML)"
cmd="crm_attribute -N cluster01 -p -v -INFINITY --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
desc="Query after updating a promotable score attribute to -INFINITY"
cmd="crm_attribute -N cluster01 -p -G"
@@ -1588,7 +1764,7 @@ function test_tools() {
desc="Query after updating a promotable score attribute to -INFINITY (XML)"
cmd="crm_attribute -N cluster01 -p -G --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
desc="Try OCF_RESOURCE_INSTANCE if -p is specified with an empty string"
cmd="crm_attribute -N cluster01 -p '' -G"
@@ -2293,20 +2469,32 @@ function test_tools() {
CIB_file_invalid_1="$test_home/cli/crm_verify_invalid_bz.xml"
CIB_file_invalid_2="$test_home/cli/crm_verify_invalid_no_stonith.xml"
- desc="Verify a file-specified invalid configuration, outputting as xml"
+ desc="Verify a file-specified invalid configuration (text output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_1'"
+ test_assert $CRM_EX_CONFIG 0
+
+ desc="Verify a file-specified invalid configuration (verbose text output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_1' --verbose"
+ test_assert $CRM_EX_CONFIG 0
+
+ desc="Verify a file-specified invalid configuration (quiet text output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_1' --quiet"
+ test_assert $CRM_EX_CONFIG 0
+
+ desc="Verify a file-specified invalid configuration (XML output)"
cmd="crm_verify --xml-file '$CIB_file_invalid_1' --output-as=xml"
test_assert_validate $CRM_EX_CONFIG 0
- desc="Verify another file-specified invalid configuration, outputting as xml"
- cmd="crm_verify --xml-file '$CIB_file_invalid_2' --output-as=xml"
+ desc="Verify a file-specified invalid configuration (verbose XML output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_1' --output-as=xml --verbose"
test_assert_validate $CRM_EX_CONFIG 0
- desc="Verbosely verify a file-specified invalid configuration, outputting as xml"
- cmd="crm_verify --xml-file '$CIB_file_invalid_1' --output-as=xml --verbose"
+ desc="Verify a file-specified invalid configuration (quiet XML output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_1' --output-as=xml --quiet"
test_assert_validate $CRM_EX_CONFIG 0
- desc="Verbosely verify another file-specified invalid configuration, outputting as xml"
- cmd="crm_verify --xml-file '$CIB_file_invalid_2' --output-as=xml --verbose"
+ desc="Verify another file-specified invalid configuration (XML output)"
+ cmd="crm_verify --xml-file '$CIB_file_invalid_2' --output-as=xml"
test_assert_validate $CRM_EX_CONFIG 0
export CIB_file="$test_home/cli/crm_mon.xml"
@@ -2815,7 +3003,7 @@ function test_validity() {
create_shadow_cib --create-empty pacemaker-1.2
orig_trace_fns="$PCMK_trace_functions"
- export PCMK_trace_functions=apply_upgrade,update_validation
+ export PCMK_trace_functions=apply_upgrade,pcmk__update_schema
cibadmin -C -o resources --xml-text '<primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>'
cibadmin -C -o resources --xml-text '<primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>'
@@ -2888,7 +3076,7 @@ test_upgrade() {
create_shadow_cib --create-empty pacemaker-2.10
orig_trace_fns="$PCMK_trace_functions"
- export PCMK_trace_functions=apply_upgrade,update_validation
+ export PCMK_trace_functions=apply_upgrade,pcmk__update_schema
desc="Set stonith-enabled=false"
cmd="crm_attribute -n stonith-enabled -v false"
@@ -3167,25 +3355,23 @@ EOF
# Ensure all command output is in portable locale for comparison
export LC_ALL="C"
test_access_render() {
- local TMPXML
+ local TMPXML=$(mktemp ${TMPDIR:-/tmp}/cts-cli.access_render.xml.XXXXXXXXXX)
- TMPXML=$(mktemp ${TMPDIR:-/tmp}/cts-cli.access_render.xml.XXXXXXXXXX)
- export CIB_shadow_dir="${shadow_dir}"
-
- $VALGRIND_CMD crm_shadow --batch --force --create-empty $shadow 2>&1
- export CIB_shadow=$shadow
+ create_shadow_cib --create-empty
# Create a test CIB that has ACL roles
cat <<EOF > "$TMPXML"
- <acls>
- <acl_role id="role-deny-acls">
- <acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
- <acl_permission id="read-rest" kind="read" xpath="/cib"/>
- </acl_role>
- <acl_target id="tony">
- <role id="role-deny-acls"/>
- </acl_target>
- </acls>
+<acls>
+ <acl_role id="role-deny-acls-write-resources">
+ <acl_permission id="deny-acls" kind="deny" xpath="/cib/configuration/acls"/>
+ <acl_permission id="write-resources" kind="write"
+ xpath="/cib/configuration/resources"/>
+ <acl_permission id="read-rest" kind="read" xpath="/cib"/>
+ </acl_role>
+ <acl_target id="tony">
+ <role id="role-deny-acls-write-resources"/>
+ </acl_target>
+</acls>
EOF
desc="Configure some ACLs"
@@ -3198,7 +3384,7 @@ EOF
unset CIB_user
- # Run cibadmin --show-access on the test CIB with different users (tony here)
+ # Run cibadmin --show-access on the test CIB as an ACL-restricted user
desc="An instance of ACLs render (into color)"
cmd="cibadmin --force --show-access=color -Q --user tony"
@@ -3230,7 +3416,7 @@ function test_feature_set() {
desc="XML output, no mixed status"
cmd="crm_mon --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
# Modify the CIB to fake that the cluster has mixed versions
desc="Fake inconsistent feature set"
@@ -3243,7 +3429,7 @@ function test_feature_set() {
desc="XML output, mixed status"
cmd="crm_mon --output-as=xml"
- test_assert $CRM_EX_OK 0
+ test_assert_validate $CRM_EX_OK 0
unset CIB_shadow_dir
}
@@ -3287,6 +3473,7 @@ done
for t in $tests; do
case "$t" in
+ access_render) ;;
agents) ;;
daemons) ;;
dates) ;;
@@ -3341,6 +3528,16 @@ for t in $tests; do
eval TMPFILE_$t="$TMPFILE"
test_$t > "$TMPFILE"
+ # @TODO Add a way to suppress this message within cibadmin, and then drop
+ # the handling here.
+ suppress="The supplied command can provide skewed result since it is run"
+ suppress="$suppress under user that also gets guarded per ACLs on their"
+ suppress="$suppress own right. Continuing since --force flag was provided."
+
+ # This giant sed replaces content expected to change for each run
+ # (timestamps, source file line numbers, etc.), build (configure options,
+ # version numbers, etc.), or platform (system messages, etc.).
+ #
# last-rc-change= is always numeric in the CIB. However, for the crm_mon
# test we also need to compare against the XML output of the crm_mon
# program. There, these are shown as human readable strings (like the
@@ -3354,10 +3551,15 @@ for t in $tests; do
-e 's/last_change time=\".*\"/last_change time=\"\"/' \
-e 's/ api-version="[^"]*"/ api-version="X"/' \
-e 's/ default="[^"]*"/ default=""/' \
+ -e 's/\(\* Possible values.*: .*\)(default: [^)]*)/\1(default: )/g' \
-e 's/ version="[^"]*"/ version=""/' \
-e 's/request=\".*\(crm_[a-zA-Z0-9]*\)/request=\"\1/' \
-e 's/crm_feature_set="[^"]*" //'\
+ -e 's/@crm_feature_set=[0-9.]*, //'\
+ -e 's/\(<change-attr name="crm_feature_set" .* value="\)[0-9.]*"/\1"/' \
-e 's/validate-with="[^"]*" //'\
+ -e 's/\(@validate-with=pacemaker-\)[0-9.]*,/\1X,/' \
+ -e 's/\(<change-attr name="validate-with" .* value="pacemaker-\)[0-9.]*"/\1X"/' \
-e 's/Created new pacemaker-.* configuration/Created new pacemaker configuration/'\
-e 's/.*\(crm_time_parse_duration\)@.*\.c:[0-9][0-9]*)/\1/g' \
-e 's/.*\(crm_time_parse_period\)@.*\.c:[0-9][0-9]*)/\1/g' \
@@ -3366,14 +3568,17 @@ for t in $tests; do
-e 's/.*\(parse_date\)@.*\.c:[0-9][0-9]*)/\1/g' \
-e 's/.*\(pcmk__.*\)@.*\.c:[0-9][0-9]*)/\1/g' \
-e 's/.*\(unpack_.*\)@.*\.c:[0-9][0-9]*)/\1/g' \
- -e 's/.*\(update_validation\)@.*\.c:[0-9][0-9]*)/\1/g' \
+ -e 's/.*\(pcmk__update_schema\)@.*\.c:[0-9][0-9]*)/\1/g' \
-e 's/.*\(apply_upgrade\)@.*\.c:[0-9][0-9]*)/\1/g' \
+ -e 's/.*\(cluster_status\)@.*\.c:[0-9][0-9]*)/\1/g' \
-e "s/ last-rc-change=['\"][-+A-Za-z0-9: ]*['\"],\{0,1\}//" \
-e 's|^/tmp/cts-cli\.shadow\.[^/]*/|/tmp/cts-cli.shadow/|' \
-e 's|"/tmp/cts-cli\.shadow\.[^/]*/|"/tmp/cts-cli.shadow/|' \
-e 's|^/tmp/cts-cli\.validity\.bad.xml\.[^:]*:|validity.bad.xml:|'\
+ -e 's|^/tmp/cts-cli\.ta_outfile\.[^:]*:|/tmp/cts-cli.ta_outfile:|' \
+ -e 's|^/tmp/cts-cli\.ta_outfile\.[^ ]* fails to validate|/tmp/cts-cli.ta_outfile fails to validate|' \
+ -e 's|^/tmp/cts-cli\.xmllint_outfile\.[^:]*:|/tmp/cts-cli.xmllint_outfile:|' \
-e 's/^Entity: line [0-9][0-9]*: //'\
- -e 's/\(validation ([0-9][0-9]* of \)[0-9][0-9]*\().*\)/\1X\2/' \
-e 's/^Migration will take effect until: .*/Migration will take effect until:/' \
-e 's/ end=\"[0-9][-+: 0-9]*Z*\"/ end=\"\"/' \
-e 's/ start=\"[0-9][-+: 0-9]*Z*\"/ start=\"\"/' \
@@ -3386,6 +3591,7 @@ for t in $tests; do
-e 's/Master/Promoted/' \
-e 's/Slave/Unpromoted/' \
-e 's/\x1b/\\x1b/' \
+ -e "/$suppress/d" \
"$TMPFILE" > "${TMPFILE}.$$"
mv -- "${TMPFILE}.$$" "$TMPFILE"
diff --git a/cts/cts-fencing.in b/cts/cts-fencing.in
index c2ed29a..6785144 100644
--- a/cts/cts-fencing.in
+++ b/cts/cts-fencing.in
@@ -133,7 +133,7 @@ class FenceTests(Tests):
'--output-as=xml -R false2 -a fence_dummy -o "mode=fail" -o "pcmk_host_list=node3" -o "pcmk_off_timeout=4"')
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 5")
# timeout is 5+1+4 = 10
- test.add_log_pattern("Total timeout set to 12")
+ test.add_log_pattern("Total timeout set to 12s")
# custom timeout _WITH_ topology
test = self.new_test("cpg_custom_timeout_2",
@@ -141,15 +141,15 @@ class FenceTests(Tests):
test.add_cmd('stonith_admin',
'--output-as=xml -R false1 -a fence_dummy -o "mode=fail" -o "pcmk_host_list=node1 node2 node3"')
test.add_cmd('stonith_admin',
- '--output-as=xml -R true1 -a fence_dummy -o "mode=pass" -o "pcmk_host_list=node3" -o "pcmk_off_timeout=1"')
+ '--output-as=xml -R true1 -a fence_dummy -o "mode=pass" -o "pcmk_host_list=node3" -o "pcmk_off_timeout=1000ms"')
test.add_cmd('stonith_admin',
- '--output-as=xml -R false2 -a fence_dummy -o "mode=fail" -o "pcmk_host_list=node3" -o "pcmk_off_timeout=4000"')
+ '--output-as=xml -R false2 -a fence_dummy -o "mode=fail" -o "pcmk_host_list=node3" -o "pcmk_off_timeout=4000s"')
test.add_cmd("stonith_admin", "--output-as=xml -r node3 -i 1 -v false1")
test.add_cmd("stonith_admin", "--output-as=xml -r node3 -i 2 -v true1")
test.add_cmd("stonith_admin", "--output-as=xml -r node3 -i 3 -v false2")
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 5")
# timeout is 5+1+4000 = 4006
- test.add_log_pattern("Total timeout set to 4807")
+ test.add_log_pattern("Total timeout set to 4807s")
def build_fence_merge_tests(self):
""" Register tests to verify when fence operations should be merged """
@@ -262,7 +262,7 @@ class FenceTests(Tests):
"--output-as=xml -R false3 -a fence_dummy -o \"mode=fail\" -o \"pcmk_host_list=node1 node2 node3\"")
if test_type["use_cpg"]:
test.add_cmd_expected_fail("stonith_admin", "--output-as=xml -F node3 -t 2", ExitStatus.TIMEOUT)
- test.add_log_pattern("Total timeout set to 7")
+ test.add_log_pattern("Total timeout set to 7s")
else:
test.add_cmd_expected_fail("stonith_admin", "--output-as=xml -F node3 -t 2", ExitStatus.ERROR)
@@ -284,7 +284,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 5")
if test_type["use_cpg"]:
- test.add_log_pattern("Total timeout set to 18")
+ test.add_log_pattern("Total timeout set to 18s")
# test what happens when we try to use a missing fence-agent.
for test_type in test_types:
@@ -313,7 +313,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -r node3 -i 1 -v true")
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 5")
- test.add_log_pattern("Total timeout set to 6")
+ test.add_log_pattern("Total timeout set to 6s")
test.add_log_pattern("targeting node3 using true returned 0")
@@ -331,7 +331,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -d node3 -i 1")
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 5")
- test.add_log_pattern("Total timeout set to 6")
+ test.add_log_pattern("Total timeout set to 6s")
test.add_log_pattern("targeting node3 using true returned 0")
# test what happens when the first fencing level has multiple devices.
@@ -350,7 +350,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -r node3 -i 2 -v true")
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 20")
- test.add_log_pattern("Total timeout set to 48")
+ test.add_log_pattern("Total timeout set to 48s")
test.add_log_pattern("targeting node3 using false returned 1")
test.add_log_pattern("targeting node3 using true returned 0")
@@ -384,7 +384,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 3")
- test.add_log_pattern("Total timeout set to 21")
+ test.add_log_pattern("Total timeout set to 21s")
test.add_log_pattern("targeting node3 using false1 returned 1")
test.add_log_pattern("targeting node3 using false2 returned 1")
test.add_log_pattern("targeting node3 using true3 returned 0")
@@ -451,7 +451,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 20")
- test.add_log_pattern("Total timeout set to 96")
+ test.add_log_pattern("Total timeout set to 96s")
test.add_log_pattern("targeting node3 using false1 returned 1")
test.add_log_pattern("targeting node3 using false2 returned ",
negative=True)
@@ -659,7 +659,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -F node3 --delay 1")
# Total fencing timeout takes all fencing delays into account.
- test.add_log_pattern("Total timeout set to 582")
+ test.add_log_pattern("Total timeout set to 582s")
# Fencing timeout for the first device takes the requested fencing delay into account.
# Fencing timeout also takes pcmk_delay_base into account.
@@ -869,7 +869,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -B node_fake -t 5")
test.add_log_pattern("Remapping multiple-device reboot targeting node_fake")
# timeout should be sum of off timeouts (1+2=3), not reboot timeouts (10+20=30)
- test.add_log_pattern("Total timeout set to 3 for peer's fencing targeting node_fake")
+ test.add_log_pattern("Total timeout set to 3s for peer's fencing targeting node_fake")
test.add_log_pattern("perform 'off' action targeting node_fake using true1")
test.add_log_pattern("perform 'off' action targeting node_fake using true2")
test.add_log_pattern("Remapped 'off' targeting node_fake complete, remapping to 'on'")
@@ -895,7 +895,7 @@ class FenceTests(Tests):
test.add_cmd("stonith_admin", "--output-as=xml -B node_fake -t 5")
test.add_log_pattern("Remapping multiple-device reboot targeting node_fake")
# timeout should be sum of off timeouts (1+2=3), not reboot timeouts (10+20=30)
- test.add_log_pattern("Total timeout set to 3 for peer's fencing targeting node_fake")
+ test.add_log_pattern("Total timeout set to 3s for peer's fencing targeting node_fake")
test.add_log_pattern("perform 'off' action targeting node_fake using true1")
test.add_log_pattern("perform 'off' action targeting node_fake using true2")
test.add_log_pattern("Remapped 'off' targeting node_fake complete, remapping to 'on'")
diff --git a/cts/cts-log-watcher.in b/cts/cts-log-watcher.in
index cee9c94..a12ecfa 100644
--- a/cts/cts-log-watcher.in
+++ b/cts/cts-log-watcher.in
@@ -43,9 +43,6 @@ if __name__ == '__main__':
skipthis=1
prefix = args[i+1]
- elif args[i] == '-t' or args[i] == '--tag':
- skipthis=1
-
if not os.access(filename, os.R_OK):
print(prefix + 'Last read: %d, limit=%d, count=%d - unreadable' % (0, limit, 0))
sys.exit(1)
diff --git a/cts/scheduler/exp/utilization-order4.exp b/cts/scheduler/exp/utilization-order4.exp
index 4a3d380..996eb1b 100644
--- a/cts/scheduler/exp/utilization-order4.exp
+++ b/cts/scheduler/exp/utilization-order4.exp
@@ -16,7 +16,7 @@
<action_set>
<rsc_op id="16" operation="migrate_to" operation_key="degllx62-vm_migrate_to_0" on_node="deglxen002" on_node_uuid="deglxen002">
<primitive id="degllx62-vm" class="ocf" provider="heartbeat" type="Xen"/>
- <attributes CRM_meta_migrate_source="deglxen002" CRM_meta_migrate_target="deglxen001" CRM_meta_name="migrate_to" CRM_meta_on_node="deglxen002" CRM_meta_on_node_uuid="deglxen002" CRM_meta_record_pending="false" CRM_meta_timeout="1800000" xmfile="/etc/xen/vm/degllx62"/>
+ <attributes CRM_meta_migrate_source="deglxen002" CRM_meta_migrate_target="deglxen001" CRM_meta_name="migrate_to" CRM_meta_on_node="deglxen002" CRM_meta_on_node_uuid="deglxen002" CRM_meta_record_pending="true" CRM_meta_timeout="1800000" xmfile="/etc/xen/vm/degllx62"/>
</rsc_op>
</action_set>
<inputs>
diff --git a/cts/scheduler/stderr/order-wrong-kind.stderr b/cts/scheduler/stderr/order-wrong-kind.stderr
index 7090368..db35666 100644
--- a/cts/scheduler/stderr/order-wrong-kind.stderr
+++ b/cts/scheduler/stderr/order-wrong-kind.stderr
@@ -1 +1 @@
-Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
+Schema validation of configuration is disabled (support for validate-with set to "none" is deprecated and will be removed in a future release) \ No newline at end of file
diff --git a/cts/scheduler/summary/order-wrong-kind.summary b/cts/scheduler/summary/order-wrong-kind.summary
index 0e00bdf..903a25c 100644
--- a/cts/scheduler/summary/order-wrong-kind.summary
+++ b/cts/scheduler/summary/order-wrong-kind.summary
@@ -1,4 +1,4 @@
-Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
+Schema validation of configuration is disabled (support for validate-with set to "none" is deprecated and will be removed in a future release)
Current cluster status:
* Node List:
* Online: [ node1 ]
diff --git a/cts/valgrind-pcmk.suppressions b/cts/valgrind-pcmk.suppressions
index a05b9db..461edc2 100644
--- a/cts/valgrind-pcmk.suppressions
+++ b/cts/valgrind-pcmk.suppressions
@@ -7,14 +7,6 @@
}
{
- Ignore option parsing
- Memcheck:Leak
- fun:realloc
- fun:crm_get_option_long
- fun:main
-}
-
-{
dlopen internals
Memcheck:Leak
fun:calloc
diff --git a/daemons/attrd/attrd_alerts.c b/daemons/attrd/attrd_alerts.c
index 495e18f..4e97743 100644
--- a/daemons/attrd/attrd_alerts.c
+++ b/daemons/attrd/attrd_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,11 +10,11 @@
#include <crm_internal.h>
#include <crm/crm.h>
#include <crm/cib/internal.h>
-#include <crm/msg_xml.h>
#include <crm/cluster/internal.h>
#include <crm/cluster/election_internal.h>
#include <crm/common/alerts_internal.h>
#include <crm/common/cib_internal.h>
+#include <crm/common/xml.h>
#include <crm/pengine/rules_internal.h>
#include <crm/lrmd_internal.h>
#include "pacemaker-attrd.h"
@@ -48,7 +48,7 @@ attrd_lrmd_connect(void)
int ret = -ENOTCONN;
for (int fails = 0; fails < max_attempts; ++fails) {
- ret = the_lrmd->cmds->connect(the_lrmd, T_ATTRD, NULL);
+ ret = the_lrmd->cmds->connect(the_lrmd, PCMK__VALUE_ATTRD, NULL);
if (ret == pcmk_ok) {
break;
}
@@ -93,11 +93,11 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
}
crmalerts = output;
- if ((crmalerts != NULL) && !pcmk__xe_is(crmalerts, XML_CIB_TAG_ALERTS)) {
- crmalerts = first_named_child(crmalerts, XML_CIB_TAG_ALERTS);
+ if ((crmalerts != NULL) && !pcmk__xe_is(crmalerts, PCMK_XE_ALERTS)) {
+ crmalerts = pcmk__xe_first_child(crmalerts, PCMK_XE_ALERTS, NULL, NULL);
}
if (!crmalerts) {
- crm_notice("CIB query result has no " XML_CIB_TAG_ALERTS " section");
+ crm_notice("CIB query result has no " PCMK_XE_ALERTS " section");
return;
}
@@ -113,7 +113,7 @@ attrd_read_options(gpointer user_data)
CRM_CHECK(the_cib != NULL, return TRUE);
call_id = the_cib->cmds->query(the_cib,
- pcmk__cib_abs_xpath_for(XML_CIB_TAG_ALERTS),
+ pcmk__cib_abs_xpath_for(PCMK_XE_ALERTS),
NULL, cib_xpath|cib_scope_local);
the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, NULL,
diff --git a/daemons/attrd/attrd_attributes.c b/daemons/attrd/attrd_attributes.c
index 388c181..974de89 100644
--- a/daemons/attrd/attrd_attributes.c
+++ b/daemons/attrd/attrd_attributes.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2022 the Pacemaker project contributors
+ * Copyright 2013-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <glib.h>
-#include <crm/msg_xml.h>
#include <crm/common/logging.h>
#include <crm/common/results.h>
#include <crm/common/strings_internal.h>
@@ -26,55 +25,56 @@ static attribute_t *
attrd_create_attribute(xmlNode *xml)
{
int is_private = 0;
- int dampen = 0;
+ long long dampen = 0;
const char *name = crm_element_value(xml, PCMK__XA_ATTR_NAME);
const char *set_type = crm_element_value(xml, PCMK__XA_ATTR_SET_TYPE);
const char *dampen_s = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
attribute_t *a = NULL;
if (set_type == NULL) {
- set_type = XML_TAG_ATTR_SETS;
+ set_type = PCMK_XE_INSTANCE_ATTRIBUTES;
}
/* Set type is meaningful only when writing to the CIB. Private
* attributes are not written.
*/
crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &is_private);
- if ((is_private != 0)
- && !pcmk__str_any_of(set_type, XML_TAG_ATTR_SETS, XML_TAG_UTILIZATION,
- NULL)) {
+ if (!is_private && !pcmk__str_any_of(set_type,
+ PCMK_XE_INSTANCE_ATTRIBUTES,
+ PCMK_XE_UTILIZATION, NULL)) {
crm_warn("Ignoring attribute %s with invalid set type %s",
pcmk__s(name, "(unidentified)"), set_type);
return NULL;
}
- a = calloc(1, sizeof(attribute_t));
- CRM_ASSERT(a != NULL);
-
- a->is_private = is_private;
- pcmk__str_update(&a->id, name);
- pcmk__str_update(&a->set_type, set_type);
+ a = pcmk__assert_alloc(1, sizeof(attribute_t));
+ a->id = pcmk__str_copy(name);
+ a->set_type = pcmk__str_copy(set_type);
a->set_id = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
- a->uuid = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID);
+ a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
a->values = pcmk__strikey_table(NULL, attrd_free_attribute_value);
- a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
- crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
+ if (is_private) {
+ attrd_set_attr_flags(a, attrd_attr_is_private);
+ }
if (dampen_s != NULL) {
dampen = crm_get_msec(dampen_s);
}
- crm_trace("Created attribute %s with %s write delay", a->id,
- (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
- if(dampen > 0) {
- a->timeout_ms = dampen;
+ if (dampen > 0) {
+ a->timeout_ms = (int) QB_MIN(dampen, INT_MAX);
a->timer = attrd_add_timer(a->id, a->timeout_ms, a);
} else if (dampen < 0) {
crm_warn("Ignoring invalid delay %s for attribute %s", dampen_s, a->id);
}
+ crm_trace("Created attribute %s with %s write delay and %s CIB user",
+ a->id,
+ ((dampen > 0)? pcmk__readable_interval(a->timeout_ms) : "no"),
+ pcmk__s(a->user, "default"));
+
g_hash_table_replace(attributes, a->id, a);
return a;
}
@@ -83,7 +83,7 @@ static int
attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr)
{
const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
- int dampen = 0;
+ long long dampen = 0;
if (dvalue == NULL) {
crm_warn("Could not update %s: peer did not specify value for delay",
@@ -100,7 +100,7 @@ attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr)
if (a->timeout_ms != dampen) {
mainloop_timer_del(a->timer);
- a->timeout_ms = dampen;
+ a->timeout_ms = (int) QB_MIN(dampen, INT_MAX);
if (dampen > 0) {
a->timer = attrd_add_timer(attr, a->timeout_ms, a);
crm_info("Update attribute %s delay to %dms (%s)",
@@ -136,20 +136,21 @@ xmlNode *
attrd_add_value_xml(xmlNode *parent, const attribute_t *a,
const attribute_value_t *v, bool force_write)
{
- xmlNode *xml = create_xml_node(parent, __func__);
+ xmlNode *xml = pcmk__xe_create(parent, __func__);
crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
+ crm_xml_add(xml, PCMK__XA_ATTR_SET_TYPE, a->set_type);
crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set_id);
- crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
pcmk__xe_add_node(xml, v->nodename, v->nodeid);
- if (v->is_remote != 0) {
+ if (pcmk_is_set(v->flags, attrd_value_remote)) {
crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
}
crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE,
+ pcmk_is_set(a->flags, attrd_attr_is_private));
+ crm_xml_add_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE, force_write);
return xml;
}
@@ -166,8 +167,7 @@ attrd_clear_value_seen(void)
while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
g_hash_table_iter_init(&vIter, a->values);
while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
- v->seen = FALSE;
- crm_trace("Clear seen flag %s[%s] = %s.", a->id, v->nodename, v->current);
+ attrd_clear_value_flags(v, attrd_value_from_peer);
}
}
}
@@ -178,9 +178,9 @@ attrd_populate_attribute(xmlNode *xml, const char *attr)
attribute_t *a = NULL;
bool update_both = false;
- const char *op = crm_element_value(xml, PCMK__XA_TASK);
+ const char *op = crm_element_value(xml, PCMK_XA_TASK);
- // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK__XA_TASK
+ // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK_XA_TASK
update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
pcmk__str_null_matches);
@@ -210,3 +210,66 @@ attrd_populate_attribute(xmlNode *xml, const char *attr)
return a;
}
+
+/*!
+ * \internal
+ * \brief Get the XML ID used to write out an attribute set
+ *
+ * \param[in] attr Attribute to get set ID for
+ * \param[in] node_state_id XML ID of node state that attribute value is for
+ *
+ * \return Newly allocated string with XML ID to use for \p attr set
+ */
+char *
+attrd_set_id(const attribute_t *attr, const char *node_state_id)
+{
+ char *set_id = NULL;
+
+ CRM_ASSERT((attr != NULL) && (node_state_id != NULL));
+
+ if (attr->set_id == NULL) {
+ /* @COMPAT This should really take the set type into account. Currently
+ * we use the same XML ID for transient attributes and utilization
+ * attributes. It doesn't cause problems because the status section is
+ * not limited by the schema in any way, but it's still unfortunate.
+ * For backward compatibility reasons, we can't change this.
+ */
+ set_id = crm_strdup_printf("%s-%s", PCMK_XE_STATUS, node_state_id);
+ } else {
+ /* @COMPAT When the user specifies a set ID for an attribute, it is the
+ * same for every node. That is less than ideal, but again, the schema
+ * doesn't enforce anything for the status section. We couldn't change
+ * it without allowing the set ID to vary per value rather than per
+ * attribute, which would break backward compatibility, pose design
+ * challenges, and potentially cause problems in rolling upgrades.
+ */
+ set_id = pcmk__str_copy(attr->set_id);
+ }
+ crm_xml_sanitize_id(set_id);
+ return set_id;
+}
+
+/*!
+ * \internal
+ * \brief Get the XML ID used to write out an attribute value
+ *
+ * \param[in] attr Attribute to get value XML ID for
+ * \param[in] node_state_id UUID of node that attribute value is for
+ *
+ * \return Newly allocated string with XML ID of \p attr value
+ */
+char *
+attrd_nvpair_id(const attribute_t *attr, const char *node_state_id)
+{
+ char *nvpair_id = NULL;
+
+ if (attr->set_id != NULL) {
+ nvpair_id = crm_strdup_printf("%s-%s", attr->set_id, attr->id);
+
+ } else {
+ nvpair_id = crm_strdup_printf(PCMK_XE_STATUS "-%s-%s",
+ node_state_id, attr->id);
+ }
+ crm_xml_sanitize_id(nvpair_id);
+ return nvpair_id;
+}
diff --git a/daemons/attrd/attrd_cib.c b/daemons/attrd/attrd_cib.c
index 80e5580..2537ade 100644
--- a/daemons/attrd/attrd_cib.c
+++ b/daemons/attrd/attrd_cib.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.
*
@@ -15,11 +15,12 @@
#include <stdlib.h>
#include <glib.h>
-#include <crm/msg_xml.h>
+#include <crm/cib/internal.h> // cib__*
#include <crm/common/logging.h>
#include <crm/common/results.h>
#include <crm/common/strings_internal.h>
#include <crm/common/xml.h>
+#include <crm/cluster/internal.h> // pcmk__get_node()
#include "pacemaker-attrd.h"
@@ -50,8 +51,10 @@ attrd_cib_updated_cb(const char *event, xmlNode *msg)
{
const xmlNode *patchset = NULL;
const char *client_name = NULL;
+ bool status_changed = false;
if (attrd_shutting_down(true)) {
+ crm_debug("Ignoring CIB change during shutdown");
return;
}
@@ -59,29 +62,32 @@ attrd_cib_updated_cb(const char *event, xmlNode *msg)
return;
}
- if (cib__element_in_patchset(patchset, XML_CIB_TAG_ALERTS)) {
+ if (cib__element_in_patchset(patchset, PCMK_XE_ALERTS)) {
mainloop_set_trigger(attrd_config_read);
}
- if (!attrd_election_won()) {
- // Don't write attributes if we're not the writer
- return;
- }
+ status_changed = cib__element_in_patchset(patchset, PCMK_XE_STATUS);
- client_name = crm_element_value(msg, F_CIB_CLIENTNAME);
+ client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME);
if (!cib__client_triggers_refresh(client_name)) {
- // The CIB is still accurate
+ /* This change came from a source that ensured the CIB is consistent
+ * with our attributes table, so we don't need to write anything out.
+ */
return;
}
- if (cib__element_in_patchset(patchset, XML_CIB_TAG_NODES)
- || cib__element_in_patchset(patchset, XML_CIB_TAG_STATUS)) {
+ if (!attrd_election_won()) {
+ // Don't write attributes if we're not the writer
+ return;
+ }
- /* An unsafe client modified the nodes or status section. Write
- * transient attributes to ensure they're up-to-date in the CIB.
+ if (status_changed || cib__element_in_patchset(patchset, PCMK_XE_NODES)) {
+ /* An unsafe client modified the PCMK_XE_NODES or PCMK_XE_STATUS
+ * section. Write transient attributes to ensure they're up-to-date in
+ * the CIB.
*/
if (client_name == NULL) {
- client_name = crm_element_value(msg, F_CIB_CLIENTID);
+ client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTID);
}
crm_notice("Updating all attributes after %s event triggered by %s",
event, pcmk__s(client_name, "(unidentified client)"));
@@ -108,7 +114,7 @@ attrd_cib_connect(int max_retry)
}
attempts++;
crm_debug("Connection attempt %d to the CIB manager", attempts);
- rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
+ rc = the_cib->cmds->signon(the_cib, PCMK__VALUE_ATTRD, cib_command);
} while ((rc != pcmk_ok) && (attempts < max_retry));
@@ -126,7 +132,8 @@ attrd_cib_connect(int max_retry)
goto cleanup;
}
- rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
+ rc = the_cib->cmds->add_notify_callback(the_cib,
+ PCMK__VALUE_CIB_DIFF_NOTIFY,
attrd_cib_updated_cb);
if (rc != pcmk_ok) {
crm_err("Could not set CIB notification callback");
@@ -144,7 +151,7 @@ void
attrd_cib_disconnect(void)
{
CRM_CHECK(the_cib != NULL, return);
- the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
+ the_cib->cmds->del_notify_callback(the_cib, PCMK__VALUE_CIB_DIFF_NOTIFY,
attrd_cib_updated_cb);
cib__clean_up_connection(&the_cib);
}
@@ -153,39 +160,44 @@ static void
attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
void *user_data)
{
- do_crm_log_unlikely(((rc != pcmk_ok)? LOG_NOTICE : LOG_DEBUG),
- "Cleared transient attributes: %s "
- CRM_XS " xpath=%s rc=%d",
- pcmk_strerror(rc), (char *) user_data, rc);
+ const char *node = pcmk__s((const char *) user_data, "a node");
+
+ if (rc == pcmk_ok) {
+ crm_info("Cleared transient node attributes for %s from CIB", node);
+ } else {
+ crm_err("Unable to clear transient node attributes for %s from CIB: %s",
+ node, pcmk_strerror(rc));
+ }
}
-#define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
+#define XPATH_TRANSIENT "//" PCMK__XE_NODE_STATE \
+ "[@" PCMK_XA_UNAME "='%s']" \
+ "/" PCMK__XE_TRANSIENT_ATTRIBUTES
/*!
* \internal
- * \brief Wipe all transient attributes for this node from the CIB
- *
- * Clear any previous transient node attributes from the CIB. This is
- * normally done by the DC's controller when this node leaves the cluster, but
- * this handles the case where the node restarted so quickly that the
- * cluster layer didn't notice.
+ * \brief Wipe all transient node attributes for a node from the CIB
*
- * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED),
- * ideally we'd skip this and sync our attributes from the writer.
- * However, currently we reject any values for us that the writer has, in
- * attrd_peer_update().
+ * \param[in] node Node to clear attributes for
*/
-static void
-attrd_erase_attrs(void)
+void
+attrd_cib_erase_transient_attrs(const char *node)
{
int call_id = 0;
- char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
+ char *xpath = NULL;
+
+ CRM_CHECK(node != NULL, return);
+
+ xpath = crm_strdup_printf(XPATH_TRANSIENT, node);
- crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
- xpath);
+ crm_debug("Clearing transient node attributes for %s from CIB using %s",
+ node, xpath);
call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
- the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
+ free(xpath);
+
+ the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE,
+ pcmk__str_copy(node),
"attrd_erase_cb", attrd_erase_cb,
free);
}
@@ -197,8 +209,17 @@ attrd_erase_attrs(void)
void
attrd_cib_init(void)
{
- // We have no attribute values in memory, wipe the CIB to match
- attrd_erase_attrs();
+ /* We have no attribute values in memory, so wipe the CIB to match. This is
+ * normally done by the DC's controller when this node leaves the cluster, but
+ * this handles the case where the node restarted so quickly that the
+ * cluster layer didn't notice.
+ *
+ * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED),
+ * ideally we'd skip this and sync our attributes from the writer.
+ * However, currently we reject any values for us that the writer has, in
+ * attrd_peer_update().
+ */
+ attrd_cib_erase_transient_attrs(attrd_cluster->uname);
// Set a trigger for reading the CIB (for the alerts section)
attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
@@ -262,19 +283,24 @@ attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *use
g_hash_table_iter_init(&iter, a->values);
while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
- do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
- free(v->requested);
- v->requested = NULL;
- if (rc != pcmk_ok) {
- a->changed = true; /* Attempt write out again */
+ if (rc == pcmk_ok) {
+ crm_info("* Wrote %s[%s]=%s",
+ a->id, peer, pcmk__s(v->requested, "(unset)"));
+ pcmk__str_update(&(v->requested), NULL);
+ } else {
+ do_crm_log(level, "* Could not write %s[%s]=%s",
+ a->id, peer, pcmk__s(v->requested, "(unset)"));
+ /* Reattempt write below if we are still the writer */
+ attrd_set_attr_flags(a, attrd_attr_changed);
}
}
- if (a->changed && attrd_election_won()) {
+ if (pcmk_is_set(a->flags, attrd_attr_changed) && attrd_election_won()) {
if (rc == pcmk_ok) {
/* We deferred a write of a new update because this update was in
* progress. Write out the new value without additional delay.
*/
+ crm_debug("Pending update for %s can be written now", a->id);
write_attribute(a, false);
/* We're re-attempting a write because the original failed; delay
@@ -320,40 +346,27 @@ static int
add_set_attr_update(const attribute_t *attr, const char *attr_id,
const char *node_id, const char *set_id, const char *value)
{
- xmlNode *update = create_xml_node(NULL, XML_CIB_TAG_STATE);
+ xmlNode *update = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE);
xmlNode *child = update;
int rc = ENOMEM;
- if (child == NULL) {
- goto done;
- }
- crm_xml_add(child, XML_ATTR_ID, node_id);
+ crm_xml_add(child, PCMK_XA_ID, node_id);
- child = create_xml_node(child, XML_TAG_TRANSIENT_NODEATTRS);
- if (child == NULL) {
- goto done;
- }
- crm_xml_add(child, XML_ATTR_ID, node_id);
+ child = pcmk__xe_create(child, PCMK__XE_TRANSIENT_ATTRIBUTES);
+ crm_xml_add(child, PCMK_XA_ID, node_id);
- child = create_xml_node(child, attr->set_type);
- if (child == NULL) {
- goto done;
- }
- crm_xml_add(child, XML_ATTR_ID, set_id);
+ child = pcmk__xe_create(child, attr->set_type);
+ crm_xml_add(child, PCMK_XA_ID, set_id);
- child = create_xml_node(child, XML_CIB_TAG_NVPAIR);
- if (child == NULL) {
- goto done;
- }
- crm_xml_add(child, XML_ATTR_ID, attr_id);
- crm_xml_add(child, XML_NVPAIR_ATTR_NAME, attr->id);
- crm_xml_add(child, XML_NVPAIR_ATTR_VALUE, value);
+ child = pcmk__xe_create(child, PCMK_XE_NVPAIR);
+ crm_xml_add(child, PCMK_XA_ID, attr_id);
+ crm_xml_add(child, PCMK_XA_NAME, attr->id);
+ crm_xml_add(child, PCMK_XA_VALUE, value);
- rc = the_cib->cmds->modify(the_cib, XML_CIB_TAG_STATUS, update,
+ rc = the_cib->cmds->modify(the_cib, PCMK_XE_STATUS, update,
cib_can_create|cib_transaction);
rc = pcmk_legacy2rc(rc);
-done:
free_xml(update);
return rc;
}
@@ -373,16 +386,16 @@ static int
add_unset_attr_update(const attribute_t *attr, const char *attr_id,
const char *node_id, const char *set_id)
{
- char *xpath = crm_strdup_printf("/" XML_TAG_CIB
- "/" XML_CIB_TAG_STATUS
- "/" XML_CIB_TAG_STATE
- "[@" XML_ATTR_ID "='%s']"
- "/" XML_TAG_TRANSIENT_NODEATTRS
- "[@" XML_ATTR_ID "='%s']"
- "/%s[@" XML_ATTR_ID "='%s']"
- "/" XML_CIB_TAG_NVPAIR
- "[@" XML_ATTR_ID "='%s' "
- "and @" XML_NVPAIR_ATTR_NAME "='%s']",
+ char *xpath = crm_strdup_printf("/" PCMK_XE_CIB
+ "/" PCMK_XE_STATUS
+ "/" PCMK__XE_NODE_STATE
+ "[@" PCMK_XA_ID "='%s']"
+ "/" PCMK__XE_TRANSIENT_ATTRIBUTES
+ "[@" PCMK_XA_ID "='%s']"
+ "/%s[@" PCMK_XA_ID "='%s']"
+ "/" PCMK_XE_NVPAIR
+ "[@" PCMK_XA_ID "='%s' "
+ "and @" PCMK_XA_NAME "='%s']",
node_id, node_id, attr->set_type, set_id,
attr_id, attr->id);
@@ -406,31 +419,17 @@ add_unset_attr_update(const attribute_t *attr, const char *attr_id,
static int
add_attr_update(const attribute_t *attr, const char *value, const char *node_id)
{
- char *set_id = NULL;
- char *attr_id = NULL;
+ char *set_id = attrd_set_id(attr, node_id);
+ char *nvpair_id = attrd_nvpair_id(attr, node_id);
int rc = pcmk_rc_ok;
- if (attr->set_id != NULL) {
- pcmk__str_update(&set_id, attr->set_id);
- } else {
- set_id = crm_strdup_printf("%s-%s", XML_CIB_TAG_STATUS, node_id);
- }
- crm_xml_sanitize_id(set_id);
-
- if (attr->uuid != NULL) {
- pcmk__str_update(&attr_id, attr->uuid);
- } else {
- attr_id = crm_strdup_printf("%s-%s", set_id, attr->id);
- }
- crm_xml_sanitize_id(attr_id);
-
- if (value != NULL) {
- rc = add_set_attr_update(attr, attr_id, node_id, set_id, value);
+ if (value == NULL) {
+ rc = add_unset_attr_update(attr, nvpair_id, node_id, set_id);
} else {
- rc = add_unset_attr_update(attr, attr_id, node_id, set_id);
+ rc = add_set_attr_update(attr, nvpair_id, node_id, set_id, value);
}
free(set_id);
- free(attr_id);
+ free(nvpair_id);
return rc;
}
@@ -454,13 +453,11 @@ send_alert_attributes_value(attribute_t *a, GHashTable *t)
static void
set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
{
- attribute_value_t *a_v = NULL;
- a_v = calloc(1, sizeof(attribute_value_t));
- CRM_ASSERT(a_v != NULL);
+ attribute_value_t *a_v = pcmk__assert_alloc(1, sizeof(attribute_value_t));
a_v->nodeid = v->nodeid;
- a_v->nodename = strdup(v->nodename);
- pcmk__str_update(&a_v->current, v->current);
+ a_v->nodename = pcmk__str_copy(v->nodename);
+ a_v->current = pcmk__str_copy(v->current);
g_hash_table_replace(t, a_v->nodename, a_v);
}
@@ -493,7 +490,7 @@ write_attribute(attribute_t *a, bool ignore_delay)
}
/* If this attribute will be written to the CIB ... */
- if (!stand_alone && !a->is_private) {
+ if (!stand_alone && !pcmk_is_set(a->flags, attrd_attr_is_private)) {
/* Defer the write if now's not a good time */
if (a->update && (a->update < last_cib_op_done)) {
crm_info("Write out of '%s' continuing: update %d considered lost",
@@ -520,21 +517,17 @@ write_attribute(attribute_t *a, bool ignore_delay)
the_cib->cmds->set_user(the_cib, a->user);
rc = the_cib->cmds->init_transaction(the_cib);
if (rc != pcmk_ok) {
- crm_err("Failed to write %s (id %s, set %s): Could not initiate "
+ crm_err("Failed to write %s (set %s): Could not initiate "
"CIB transaction",
- a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
+ a->id, pcmk__s(a->set_id, "unspecified"));
goto done;
}
}
- /* Attribute will be written shortly, so clear changed flag */
- a->changed = false;
-
- /* We will check all peers' uuids shortly, so initialize this to false */
- a->unknown_peer_uuids = false;
-
- /* Attribute will be written shortly, so clear forced write flag */
- a->force_write = FALSE;
+ /* Attribute will be written shortly, so clear changed flag and force
+ * write flag, and initialize UUID missing flag to false.
+ */
+ attrd_clear_attr_flags(a, attrd_attr_changed|attrd_attr_uuid_missing|attrd_attr_force_write);
/* Make the table for the attribute trap */
alert_attribute_value = pcmk__strikey_table(NULL,
@@ -543,79 +536,80 @@ write_attribute(attribute_t *a, bool ignore_delay)
/* Iterate over each peer value of this attribute */
g_hash_table_iter_init(&iter, a->values);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
- crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename,
- CRM_GET_PEER_ANY);
+ const char *uuid = NULL;
- /* If the value's peer info does not correspond to a peer, ignore it */
- if (peer == NULL) {
- crm_notice("Cannot update %s[%s]=%s because peer not known",
- a->id, v->nodename, v->current);
- continue;
- }
+ if (pcmk_is_set(v->flags, attrd_value_remote)) {
+ /* If this is a Pacemaker Remote node, the node's UUID is the same
+ * as its name, which we already have.
+ */
+ uuid = v->nodename;
- /* If we're just learning the peer's node id, remember it */
- if (peer->id && (v->nodeid == 0)) {
- crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
- v->nodeid = peer->id;
+ } else {
+ // This will create a cluster node cache entry if none exists
+ crm_node_t *peer = pcmk__get_node(v->nodeid, v->nodename, NULL,
+ pcmk__node_search_any);
+
+ uuid = peer->uuid;
+
+ // Remember peer's node ID if we're just now learning it
+ if ((peer->id != 0) && (v->nodeid == 0)) {
+ crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
+ v->nodeid = peer->id;
+ }
}
/* If this is a private attribute, no update needs to be sent */
- if (stand_alone || a->is_private) {
+ if (stand_alone || pcmk_is_set(a->flags, attrd_attr_is_private)) {
private_updates++;
continue;
}
- /* If the peer is found, but its uuid is unknown, defer write */
- if (peer->uuid == NULL) {
- a->unknown_peer_uuids = true;
- crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
- "(will retry if learned)",
+ // Defer write if this is a cluster node that's never been seen
+ if (uuid == NULL) {
+ attrd_set_attr_flags(a, attrd_attr_uuid_missing);
+ crm_notice("Cannot update %s[%s]='%s' now because node's UUID is "
+ "unknown (will retry if learned)",
a->id, v->nodename, v->current);
continue;
}
// Update this value as part of the CIB transaction we're building
- rc = add_attr_update(a, v->current, peer->uuid);
+ rc = add_attr_update(a, v->current, uuid);
if (rc != pcmk_rc_ok) {
- crm_err("Failed to update %s[%s]=%s (peer known as %s, UUID %s, "
- "ID %" PRIu32 "/%" PRIu32 "): %s",
- a->id, v->nodename, v->current, peer->uname, peer->uuid,
- peer->id, v->nodeid, pcmk_rc_str(rc));
+ crm_err("Failed to update %s[%s]='%s': %s "
+ CRM_XS " node uuid=%s id=%" PRIu32,
+ a->id, v->nodename, v->current, pcmk_rc_str(rc),
+ uuid, v->nodeid);
continue;
}
- crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID "
- "%" PRIu32 "/%" PRIu32 ")",
- a->id, v->nodename, v->current,
- peer->uname, peer->uuid, peer->id, v->nodeid);
+ crm_debug("Writing %s[%s]=%s (node-state-id=%s node-id=%" PRIu32 ")",
+ a->id, v->nodename, pcmk__s(v->current, "(unset)"),
+ uuid, v->nodeid);
cib_updates++;
/* Preservation of the attribute to transmit alert */
set_alert_attribute_value(alert_attribute_value, v);
- free(v->requested);
- v->requested = NULL;
- if (v->current) {
- v->requested = strdup(v->current);
- }
+ // Save this value so we can log it when write completes
+ pcmk__str_update(&(v->requested), v->current);
}
if (private_updates) {
- crm_info("Processed %d private change%s for %s, id=%s, set=%s",
+ crm_info("Processed %d private change%s for %s (set %s)",
private_updates, pcmk__plural_s(private_updates),
- a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
+ a->id, pcmk__s(a->set_id, "unspecified"));
}
if (cib_updates > 0) {
- char *id = NULL;
+ char *id = pcmk__str_copy(a->id);
// Commit transaction
a->update = the_cib->cmds->end_transaction(the_cib, true, cib_none);
- crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
+ crm_info("Sent CIB request %d with %d change%s for %s (set %s)",
a->update, cib_updates, pcmk__plural_s(cib_updates),
- a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
+ a->id, pcmk__s(a->set_id, "unspecified"));
- pcmk__str_update(&id, a->id);
if (the_cib->cmds->register_callback_full(the_cib, a->update,
CIB_OP_TIMEOUT_S, FALSE, id,
"attrd_cib_callback",
@@ -653,18 +647,20 @@ attrd_write_attributes(uint32_t options)
pcmk_is_set(options, attrd_write_all)? "all" : "changed");
g_hash_table_iter_init(&iter, attributes);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
- if (!pcmk_is_set(options, attrd_write_all) && a->unknown_peer_uuids) {
+ if (!pcmk_is_set(options, attrd_write_all) &&
+ pcmk_is_set(a->flags, attrd_attr_uuid_missing)) {
// Try writing this attribute again, in case peer ID was learned
- a->changed = true;
- } else if (a->force_write) {
+ attrd_set_attr_flags(a, attrd_attr_changed);
+ } else if (pcmk_is_set(a->flags, attrd_attr_force_write)) {
/* If the force_write flag is set, write the attribute. */
- a->changed = true;
+ attrd_set_attr_flags(a, attrd_attr_changed);
}
- if (pcmk_is_set(options, attrd_write_all) || a->changed) {
+ if (pcmk_is_set(options, attrd_write_all) ||
+ pcmk_is_set(a->flags, attrd_attr_changed)) {
bool ignore_delay = pcmk_is_set(options, attrd_write_no_delay);
- if (a->force_write) {
+ if (pcmk_is_set(a->flags, attrd_attr_force_write)) {
// Always ignore delay when forced write flag is set
ignore_delay = true;
}
diff --git a/daemons/attrd/attrd_corosync.c b/daemons/attrd/attrd_corosync.c
index 86dc67b..fb3b4e5 100644
--- a/daemons/attrd/attrd_corosync.c
+++ b/daemons/attrd/attrd_corosync.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.
*
@@ -19,19 +19,19 @@
#include <crm/common/logging.h>
#include <crm/common/results.h>
#include <crm/common/strings_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include "pacemaker-attrd.h"
static xmlNode *
attrd_confirmation(int callid)
{
- xmlNode *node = create_xml_node(NULL, __func__);
+ xmlNode *node = pcmk__xe_create(NULL, __func__);
- crm_xml_add(node, F_TYPE, T_ATTRD);
- crm_xml_add(node, F_ORIG, get_local_node_name());
- crm_xml_add(node, PCMK__XA_TASK, PCMK__ATTRD_CMD_CONFIRM);
- crm_xml_add_int(node, XML_LRM_ATTR_CALLID, callid);
+ crm_xml_add(node, PCMK__XA_T, PCMK__VALUE_ATTRD);
+ crm_xml_add(node, PCMK__XA_SRC, pcmk__cluster_local_node_name());
+ crm_xml_add(node, PCMK_XA_TASK, PCMK__ATTRD_CMD_CONFIRM);
+ crm_xml_add_int(node, PCMK__XA_CALL_ID, callid);
return node;
}
@@ -39,7 +39,7 @@ attrd_confirmation(int callid)
static void
attrd_peer_message(crm_node_t *peer, xmlNode *xml)
{
- const char *election_op = crm_element_value(xml, F_CRM_TASK);
+ const char *election_op = crm_element_value(xml, PCMK__XA_CRM_TASK);
if (election_op) {
attrd_handle_election_op(peer, xml);
@@ -64,7 +64,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
.result = PCMK__UNKNOWN_RESULT,
};
- request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK);
+ request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK);
CRM_CHECK(request.op != NULL, return);
attrd_handle_request(&request);
@@ -81,7 +81,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
* response so the originating peer knows what they're a confirmation
* for.
*/
- crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid);
+ crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid);
reply = attrd_confirmation(callid);
/* And then send the confirmation back to the originating peer. This
@@ -106,22 +106,22 @@ attrd_cpg_dispatch(cpg_handle_t handle,
uint32_t kind = 0;
xmlNode *xml = NULL;
const char *from = NULL;
- char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
+ char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
if(data == NULL) {
return;
}
if (kind == crm_class_cluster) {
- xml = string2xml(data);
+ xml = pcmk__xml_parse(data);
}
if (xml == NULL) {
crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data);
} else {
- crm_node_t *peer = crm_get_peer(nodeid, from);
-
- attrd_peer_message(peer, xml);
+ attrd_peer_message(pcmk__get_node(nodeid, from, NULL,
+ pcmk__node_search_cluster_member),
+ xml);
}
free_xml(xml);
@@ -143,86 +143,24 @@ attrd_cpg_destroy(gpointer unused)
/*!
* \internal
- * \brief Override an attribute sync with a local value
- *
- * Broadcast the local node's value for an attribute that's different from the
- * value provided in a peer's attribute synchronization response. This ensures a
- * node's values for itself take precedence and all peers are kept in sync.
+ * \brief Broadcast an update for a single attribute value
*
- * \param[in] a Attribute entry to override
- *
- * \return Local instance of attribute value
+ * \param[in] a Attribute to broadcast
+ * \param[in] v Attribute value to broadcast
*/
-static attribute_value_t *
-broadcast_local_value(const attribute_t *a)
-{
- attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
- xmlNode *sync = create_xml_node(NULL, __func__);
-
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
- attrd_add_value_xml(sync, a, v, false);
- attrd_send_message(NULL, sync, false);
- free_xml(sync);
- return v;
-}
-
-/*!
- * \internal
- * \brief Ensure a Pacemaker Remote node is in the correct peer cache
- *
- * \param[in] node_name Name of Pacemaker Remote node to check
- */
-static void
-cache_remote_node(const char *node_name)
+void
+attrd_broadcast_value(const attribute_t *a, const attribute_value_t *v)
{
- /* If we previously assumed this node was an unseen cluster node,
- * remove its entry from the cluster peer cache.
- */
- crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name, NULL);
+ xmlNode *op = pcmk__xe_create(NULL, PCMK_XE_OP);
- if (dup && (dup->uuid == NULL)) {
- reap_crm_member(0, node_name);
- }
-
- // Ensure node is in the remote peer cache
- CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
+ crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
+ attrd_add_value_xml(op, a, v, false);
+ attrd_send_message(NULL, op, false);
+ free_xml(op);
}
#define state_text(state) pcmk__s((state), "in unknown state")
-/*!
- * \internal
- * \brief Return host's hash table entry (creating one if needed)
- *
- * \param[in,out] values Hash table of values
- * \param[in] host Name of peer to look up
- * \param[in] xml XML describing the attribute
- *
- * \return Pointer to new or existing hash table entry
- */
-static attribute_value_t *
-attrd_lookup_or_create_value(GHashTable *values, const char *host,
- const xmlNode *xml)
-{
- attribute_value_t *v = g_hash_table_lookup(values, host);
- int is_remote = 0;
-
- crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
- if (is_remote) {
- cache_remote_node(host);
- }
-
- if (v == NULL) {
- v = calloc(1, sizeof(attribute_value_t));
- CRM_ASSERT(v != NULL);
-
- pcmk__str_update(&v->nodename, host);
- v->is_remote = is_remote;
- g_hash_table_replace(values, v->nodename, v);
- }
- return(v);
-}
-
static void
attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
{
@@ -254,7 +192,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
*/
if (attrd_election_won()
&& !pcmk_is_set(peer->flags, crm_remote_node)) {
- attrd_peer_sync(peer, NULL);
+ attrd_peer_sync(peer);
}
} else {
// Remove all attribute values associated with lost nodes
@@ -269,17 +207,14 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
attrd_remove_voter(peer);
attrd_remove_peer_protocol_ver(peer->uname);
attrd_do_not_expect_from_peer(peer->uname);
-
- // Ensure remote nodes that come up are in the remote node cache
- } else if (!gone && is_remote) {
- cache_remote_node(peer->uname);
}
}
static void
record_peer_nodeid(attribute_value_t *v, const char *host)
{
- crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
+ crm_node_t *known_peer = pcmk__get_node(v->nodeid, host, NULL,
+ pcmk__node_search_cluster_member);
crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid);
if (attrd_election_won()) {
@@ -287,34 +222,63 @@ record_peer_nodeid(attribute_value_t *v, const char *host)
}
}
+#define readable_value(rv_v) pcmk__s((rv_v)->current, "(unset)")
+
+#define readable_peer(p) \
+ (((p) == NULL)? "all peers" : pcmk__s((p)->uname, "unknown peer"))
+
static void
update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml,
const char *attr, const char *value, const char *host,
- bool filter, int is_force_write)
+ bool filter)
{
+ int is_remote = 0;
+ bool changed = false;
attribute_value_t *v = NULL;
- v = attrd_lookup_or_create_value(a->values, host, xml);
+ // Create entry for value if not already existing
+ v = g_hash_table_lookup(a->values, host);
+ if (v == NULL) {
+ v = pcmk__assert_alloc(1, sizeof(attribute_value_t));
+
+ v->nodename = pcmk__str_copy(host);
+ g_hash_table_replace(a->values, v->nodename, v);
+ }
+
+ // If value is for a Pacemaker Remote node, remember that
+ crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
+ if (is_remote) {
+ attrd_set_value_flags(v, attrd_value_remote);
+ CRM_ASSERT(pcmk__cluster_lookup_remote_node(host) != NULL);
+ }
- if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
- && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
+ // Check whether the value changed
+ changed = !pcmk__str_eq(v->current, value, pcmk__str_casei);
+ if (changed && filter && pcmk__str_eq(host, attrd_cluster->uname,
+ pcmk__str_casei)) {
+ /* Broadcast the local value for an attribute that differs from the
+ * value provided in a peer's attribute synchronization response. This
+ * ensures a node's values for itself take precedence and all peers are
+ * kept in sync.
+ */
+ v = g_hash_table_lookup(a->values, attrd_cluster->uname);
crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
- attr, host, v->current, value, peer->uname);
- v = broadcast_local_value(a);
+ attr, host, readable_value(v), value, peer->uname);
+ attrd_broadcast_value(a, v);
- } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
+ } else if (changed) {
crm_notice("Setting %s[%s]%s%s: %s -> %s "
CRM_XS " from %s with %s write delay",
attr, host, a->set_type ? " in " : "",
- pcmk__s(a->set_type, ""), pcmk__s(v->current, "(unset)"),
+ pcmk__s(a->set_type, ""), readable_value(v),
pcmk__s(value, "(unset)"), peer->uname,
(a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
pcmk__str_update(&v->current, value);
- a->changed = true;
+ attrd_set_attr_flags(a, attrd_attr_changed);
if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
- && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
+ && pcmk__str_eq(attr, PCMK__NODE_ATTR_SHUTDOWN, pcmk__str_none)) {
if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
attrd_set_requesting_shutdown();
@@ -326,30 +290,37 @@ update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml,
// Write out new value or start dampening timer
if (a->timeout_ms && a->timer) {
- crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
+ crm_trace("Delaying write of %s %s for dampening",
+ attr, pcmk__readable_interval(a->timeout_ms));
mainloop_timer_start(a->timer);
} else {
attrd_write_or_elect_attribute(a);
}
} else {
+ int is_force_write = 0;
+
+ crm_element_value_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE,
+ &is_force_write);
+
if (is_force_write == 1 && a->timeout_ms && a->timer) {
/* Save forced writing and set change flag. */
/* The actual attribute is written by Writer after election. */
- crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)",
- attr, host, peer->uname, value);
- a->force_write = TRUE;
+ crm_trace("%s[%s] from %s is unchanged (%s), forcing write",
+ attr, host, peer->uname, pcmk__s(value, "unset"));
+ attrd_set_attr_flags(a, attrd_attr_force_write);
} else {
- crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
+ crm_trace("%s[%s] from %s is unchanged (%s)",
+ attr, host, peer->uname, pcmk__s(value, "unset"));
}
}
- /* Set the seen flag for attribute processing held only in the own node. */
- v->seen = TRUE;
+ // This allows us to later detect local values that peer doesn't know about
+ attrd_set_value_flags(v, attrd_value_from_peer);
/* If this is a cluster node whose node ID we are learning, remember it */
- if ((v->nodeid == 0) && (v->is_remote == FALSE)
- && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
+ if ((v->nodeid == 0) && !pcmk_is_set(v->flags, attrd_value_remote)
+ && (crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID,
(int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
record_peer_nodeid(v, host);
}
@@ -361,16 +332,13 @@ attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter)
attribute_t *a = NULL;
const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
- int is_force_write = 0;
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
if (attr == NULL) {
crm_warn("Could not update attribute: peer did not specify name");
return;
}
- crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
-
a = attrd_populate_attribute(xml, attr);
if (a == NULL) {
return;
@@ -381,16 +349,16 @@ attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter)
GHashTableIter vIter;
crm_debug("Setting %s for all hosts to %s", attr, value);
- xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
+ pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_HOST_ID);
g_hash_table_iter_init(&vIter, a->values);
while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
- update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
+ update_attr_on_host(a, peer, xml, attr, value, host, filter);
}
} else {
// Update attribute value for the given host
- update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
+ update_attr_on_host(a, peer, xml, attr, value, host, filter);
}
/* If this is a message from some attrd instance broadcasting its protocol
@@ -412,13 +380,18 @@ broadcast_unseen_local_values(void)
g_hash_table_iter_init(&aIter, attributes);
while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
+
g_hash_table_iter_init(&vIter, a->values);
while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
- if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
- pcmk__str_casei)) {
+
+ if (!pcmk_is_set(v->flags, attrd_value_from_peer)
+ && pcmk__str_eq(v->nodename, attrd_cluster->uname,
+ pcmk__str_casei)) {
+ crm_trace("* %s[%s]='%s' is local-only",
+ a->id, v->nodename, readable_value(v));
if (sync == NULL) {
- sync = create_xml_node(NULL, __func__);
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+ sync = pcmk__xe_create(NULL, __func__);
+ crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
}
attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
}
@@ -435,17 +408,21 @@ broadcast_unseen_local_values(void)
int
attrd_cluster_connect(void)
{
+ int rc = pcmk_rc_ok;
+
attrd_cluster = pcmk_cluster_new();
- attrd_cluster->destroy = attrd_cpg_destroy;
- attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
- attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
+ pcmk_cluster_set_destroy_fn(attrd_cluster, attrd_cpg_destroy);
+ pcmk_cpg_set_deliver_fn(attrd_cluster, attrd_cpg_dispatch);
+ pcmk_cpg_set_confchg_fn(attrd_cluster, pcmk__cpg_confchg_cb);
- crm_set_status_callback(&attrd_peer_change_cb);
+ pcmk__cluster_set_status_callback(&attrd_peer_change_cb);
- if (crm_cluster_connect(attrd_cluster) == FALSE) {
+ rc = pcmk_cluster_connect(attrd_cluster);
+ rc = pcmk_rc2legacy(rc);
+ if (rc != pcmk_ok) {
crm_err("Cluster connection failed");
- return -ENOTCONN;
+ return rc;
}
return pcmk_ok;
}
@@ -455,15 +432,19 @@ attrd_peer_clear_failure(pcmk__request_t *request)
{
xmlNode *xml = request->xml;
const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
- const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
- const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
- guint interval_ms = crm_parse_interval_spec(interval_spec);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
+ const char *op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION);
+ const char *interval_spec = crm_element_value(xml,
+ PCMK__XA_ATTR_CLEAR_INTERVAL);
+ guint interval_ms = 0U;
char *attr = NULL;
GHashTableIter iter;
regex_t regex;
- crm_node_t *peer = crm_get_peer(0, request->peer);
+ crm_node_t *peer = pcmk__get_node(0, request->peer, NULL,
+ pcmk__node_search_cluster_member);
+
+ pcmk_parse_interval_spec(interval_spec, &interval_ms);
if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
crm_info("Ignoring invalid request to clear failures for %s",
@@ -471,10 +452,10 @@ attrd_peer_clear_failure(pcmk__request_t *request)
return;
}
- crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
+ crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
/* Make sure value is not set, so we delete */
- xml_remove_prop(xml, PCMK__XA_ATTR_VALUE);
+ pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE);
g_hash_table_iter_init(&iter, attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
@@ -492,7 +473,7 @@ attrd_peer_clear_failure(pcmk__request_t *request)
* \internal
* \brief Load attributes from a peer sync response
*
- * \param[in] peer Peer that sent clear request
+ * \param[in] peer Peer that sent sync response
* \param[in] peer_won Whether peer is the attribute writer
* \param[in,out] xml Request XML
*/
@@ -510,11 +491,11 @@ attrd_peer_sync_response(const crm_node_t *peer, bool peer_won, xmlNode *xml)
}
// Process each attribute update in the sync response
- for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
- child = pcmk__xml_next(child)) {
+ for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL);
+ child != NULL; child = pcmk__xe_next(child)) {
+
attrd_peer_update(peer, child,
- crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
- true);
+ crm_element_value(child, PCMK__XA_ATTR_HOST), true);
}
if (peer_won) {
@@ -540,7 +521,9 @@ attrd_peer_remove(const char *host, bool uncache, const char *source)
GHashTableIter aIter;
CRM_CHECK(host != NULL, return);
- crm_notice("Removing all %s attributes for peer %s", host, source);
+ crm_notice("Removing all %s attributes for node %s "
+ CRM_XS " %s reaping node from cache",
+ host, source, (uncache? "and" : "without"));
g_hash_table_iter_init(&aIter, attributes);
while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
@@ -550,33 +533,40 @@ attrd_peer_remove(const char *host, bool uncache, const char *source)
}
if (uncache) {
- crm_remote_peer_cache_remove(host);
- reap_crm_member(0, host);
+ pcmk__purge_node_from_cache(host, 0);
}
}
+/*!
+ * \internal
+ * \brief Send all known attributes and values to a peer
+ *
+ * \param[in] peer Peer to send sync to (if NULL, broadcast to all peers)
+ */
void
-attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
+attrd_peer_sync(crm_node_t *peer)
{
GHashTableIter aIter;
GHashTableIter vIter;
attribute_t *a = NULL;
attribute_value_t *v = NULL;
- xmlNode *sync = create_xml_node(NULL, __func__);
+ xmlNode *sync = pcmk__xe_create(NULL, __func__);
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+ crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
g_hash_table_iter_init(&aIter, attributes);
while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
g_hash_table_iter_init(&vIter, a->values);
while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
- crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
+ crm_debug("Syncing %s[%s]='%s' to %s",
+ a->id, v->nodename, readable_value(v),
+ readable_peer(peer));
attrd_add_value_xml(sync, a, v, false);
}
}
- crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
+ crm_debug("Syncing values to %s", readable_peer(peer));
attrd_send_message(peer, sync, false);
free_xml(sync);
}
@@ -589,9 +579,10 @@ attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
CRM_CHECK((peer != NULL) && (xml != NULL), return);
if (xml->children != NULL) {
- for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
- child = crm_next_same_xml(child)) {
- attrd_copy_xml_attributes(xml, child);
+ for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL, NULL);
+ child != NULL; child = pcmk__xe_next_same(child)) {
+
+ pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
attrd_peer_update_one(peer, child, filter);
if (attrd_request_has_sync_point(child)) {
diff --git a/daemons/attrd/attrd_elections.c b/daemons/attrd/attrd_elections.c
index 82fbe8a..0abd9c0 100644
--- a/daemons/attrd/attrd_elections.c
+++ b/daemons/attrd/attrd_elections.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,9 +8,9 @@
*/
#include <crm_internal.h>
-#include <crm/msg_xml.h>
#include <crm/cluster.h>
#include <crm/cluster/election_internal.h>
+#include <crm/common/xml.h>
#include "pacemaker-attrd.h"
@@ -23,7 +23,7 @@ attrd_election_cb(gpointer user_data)
attrd_declare_winner();
/* Update the peers after an election */
- attrd_peer_sync(NULL, NULL);
+ attrd_peer_sync(NULL);
/* After winning an election, update the CIB with the values of all
* attributes as the winner knows them.
@@ -35,7 +35,7 @@ attrd_election_cb(gpointer user_data)
void
attrd_election_init(void)
{
- writer = election_init(T_ATTRD, attrd_cluster->uname, 120000,
+ writer = election_init(PCMK__VALUE_ATTRD, attrd_cluster->uname, 120000,
attrd_election_cb);
}
@@ -69,7 +69,7 @@ attrd_handle_election_op(const crm_node_t *peer, xmlNode *xml)
enum election_result rc = 0;
enum election_result previous = election_state(writer);
- crm_xml_add(xml, F_CRM_HOST_FROM, peer->uname);
+ crm_xml_add(xml, PCMK__XA_SRC, peer->uname);
// Don't become writer if we're shutting down
rc = election_count_vote(writer, xml, !attrd_shutting_down(false));
diff --git a/daemons/attrd/attrd_ipc.c b/daemons/attrd/attrd_ipc.c
index 05c4a69..0a2688e 100644
--- a/daemons/attrd/attrd_ipc.c
+++ b/daemons/attrd/attrd_ipc.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,13 +16,13 @@
#include <crm/cluster.h>
#include <crm/cluster/internal.h>
-#include <crm/msg_xml.h>
#include <crm/common/acl_internal.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/logging.h>
#include <crm/common/results.h>
#include <crm/common/strings_internal.h>
#include <crm/common/util.h>
+#include <crm/common/xml.h>
#include "pacemaker-attrd.h"
@@ -32,22 +32,19 @@ static qb_ipcs_service_t *ipcs = NULL;
* \internal
* \brief Build the XML reply to a client query
*
- * param[in] attr Name of requested attribute
- * param[in] host Name of requested host (or NULL for all hosts)
+ * \param[in] attr Name of requested attribute
+ * \param[in] host Name of requested host (or NULL for all hosts)
*
* \return New XML reply
* \note Caller is responsible for freeing the resulting XML
*/
static xmlNode *build_query_reply(const char *attr, const char *host)
{
- xmlNode *reply = create_xml_node(NULL, __func__);
+ xmlNode *reply = pcmk__xe_create(NULL, __func__);
attribute_t *a;
- if (reply == NULL) {
- return NULL;
- }
- crm_xml_add(reply, F_TYPE, T_ATTRD);
- crm_xml_add(reply, F_SUBTYPE, PCMK__ATTRD_CMD_QUERY);
+ crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_ATTRD);
+ crm_xml_add(reply, PCMK__XA_SUBT, PCMK__ATTRD_CMD_QUERY);
crm_xml_add(reply, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
/* If desired attribute exists, add its value(s) to the reply */
@@ -67,11 +64,7 @@ static xmlNode *build_query_reply(const char *attr, const char *host)
/* If a specific node was requested, add its value */
if (host) {
v = g_hash_table_lookup(a->values, host);
- host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
- if (host_value == NULL) {
- free_xml(reply);
- return NULL;
- }
+ host_value = pcmk__xe_create(reply, PCMK_XE_NODE);
pcmk__xe_add_node(host_value, host, 0);
crm_xml_add(host_value, PCMK__XA_ATTR_VALUE,
(v? v->current : NULL));
@@ -82,11 +75,7 @@ static xmlNode *build_query_reply(const char *attr, const char *host)
g_hash_table_iter_init(&iter, a->values);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
- host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
- if (host_value == NULL) {
- free_xml(reply);
- return NULL;
- }
+ host_value = pcmk__xe_create(reply, PCMK_XE_NODE);
pcmk__xe_add_node(host_value, v->nodename, 0);
crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, v->current);
}
@@ -111,11 +100,11 @@ attrd_client_clear_failure(pcmk__request_t *request)
}
rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
- op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
- interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
+ op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION);
+ interval_spec = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_INTERVAL);
/* Map this to an update */
- crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
+ crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
/* Add regular expression matching desired attributes */
@@ -126,22 +115,23 @@ attrd_client_clear_failure(pcmk__request_t *request)
pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
} else {
- guint interval_ms = crm_parse_interval_spec(interval_spec);
+ guint interval_ms = 0U;
+ pcmk_parse_interval_spec(interval_spec, &interval_ms);
pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
rsc, op, interval_ms);
}
- crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, pattern);
+ crm_xml_add(xml, PCMK__XA_ATTR_REGEX, pattern);
free(pattern);
} else {
- crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, ATTRD_RE_CLEAR_ALL);
+ crm_xml_add(xml, PCMK__XA_ATTR_REGEX, ATTRD_RE_CLEAR_ALL);
}
/* Make sure attribute and value are not set, so we delete via regex */
- xml_remove_prop(xml, PCMK__XA_ATTR_NAME);
- xml_remove_prop(xml, PCMK__XA_ATTR_VALUE);
+ pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_NAME);
+ pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE);
return attrd_client_update(request);
}
@@ -152,7 +142,7 @@ attrd_client_peer_remove(pcmk__request_t *request)
xmlNode *xml = request->xml;
// Host and ID are not used in combination, rather host has precedence
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
char *host_alloc = NULL;
attrd_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags);
@@ -160,18 +150,19 @@ attrd_client_peer_remove(pcmk__request_t *request)
if (host == NULL) {
int nodeid = 0;
- crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, &nodeid);
+ crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID, &nodeid);
if (nodeid > 0) {
- crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL,
- NULL);
+ crm_node_t *node = NULL;
char *host_alloc = NULL;
+ node = pcmk__search_node_caches(nodeid, NULL,
+ pcmk__node_search_cluster_member);
if (node && node->uname) {
// Use cached name if available
host = node->uname;
} else {
// Otherwise ask cluster layer
- host_alloc = get_node_name(nodeid);
+ host_alloc = pcmk__cluster_node_name(nodeid);
host = host_alloc;
}
pcmk__xe_add_node(xml, host, 0);
@@ -211,8 +202,8 @@ attrd_client_query(pcmk__request_t *request)
}
/* Build the XML reply */
- reply = build_query_reply(attr, crm_element_value(query,
- PCMK__XA_ATTR_NODE_NAME));
+ reply = build_query_reply(attr,
+ crm_element_value(query, PCMK__XA_ATTR_HOST));
if (reply == NULL) {
pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
"Could not respond to query from %s: could not create XML reply",
@@ -241,7 +232,7 @@ attrd_client_refresh(pcmk__request_t *request)
static void
handle_missing_host(xmlNode *xml)
{
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
if (host == NULL) {
crm_trace("Inferring host");
@@ -270,16 +261,16 @@ expand_regexes(xmlNode *xml, const char *attr, const char *value, const char *re
int status = regexec(&r_patt, attr, 0, NULL, 0);
if (status == 0) {
- xmlNode *child = create_xml_node(xml, XML_ATTR_OP);
+ xmlNode *child = pcmk__xe_create(xml, PCMK_XE_OP);
crm_trace("Matched %s with %s", attr, regex);
matched = true;
- /* Copy all the attributes from the parent over, but remove the
- * regex and replace it with the name.
+ /* Copy all the non-conflicting attributes from the parent over,
+ * but remove the regex and replace it with the name.
*/
- attrd_copy_xml_attributes(xml, child);
- xml_remove_prop(child, PCMK__XA_ATTR_PATTERN);
+ pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
+ pcmk__xe_remove_attr(child, PCMK__XA_ATTR_REGEX);
crm_xml_add(child, PCMK__XA_ATTR_NAME, attr);
}
}
@@ -310,7 +301,7 @@ handle_regexes(pcmk__request_t *request)
const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
- const char *regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN);
+ const char *regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
rc = expand_regexes(xml, attr, value, regex);
@@ -344,7 +335,7 @@ handle_value_expansion(const char **value, xmlNode *xml, const char *op,
attribute_value_t *v = NULL;
if (a) {
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
v = g_hash_table_lookup(a->values, host);
}
@@ -416,8 +407,10 @@ attrd_client_update(pcmk__request_t *request)
* we also need to apply all the transformations in this function
* to the children since they don't happen anywhere else.
*/
- for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
- child = crm_next_same_xml(child)) {
+ for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL,
+ NULL);
+ child != NULL; child = pcmk__xe_next_same(child)) {
+
attr = crm_element_value(child, PCMK__XA_ATTR_NAME);
value = crm_element_value(child, PCMK__XA_ATTR_VALUE);
@@ -443,7 +436,7 @@ attrd_client_update(pcmk__request_t *request)
* up into individual messages and call attrd_client_update on
* each one.
*/
- pcmk__xe_foreach_child(xml, XML_ATTR_OP, send_child_update, request);
+ pcmk__xe_foreach_child(xml, PCMK_XE_OP, send_child_update, request);
request->xml = orig_xml;
}
@@ -452,7 +445,7 @@ attrd_client_update(pcmk__request_t *request)
attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
- regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN);
+ regex = crm_element_value(xml, PCMK__XA_ATTR_REGEX);
if (handle_regexes(request) != pcmk_rc_ok) {
/* Error handling was already dealt with in handle_regexes, so just return. */
@@ -473,7 +466,8 @@ attrd_client_update(pcmk__request_t *request)
return NULL;
}
- crm_debug("Broadcasting %s[%s]=%s%s", attr, crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME),
+ crm_debug("Broadcasting %s[%s]=%s%s",
+ attr, crm_element_value(xml, PCMK__XA_ATTR_HOST),
value, (attrd_election_won()? " (writer)" : ""));
send_update_msg_to_cluster(request, xml);
@@ -498,11 +492,11 @@ attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
if (attrd_shutting_down(false)) {
crm_info("Ignoring new connection from pid %d during shutdown",
pcmk__client_pid(c));
- return -EPERM;
+ return -ECONNREFUSED;
}
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return pcmk_ok;
}
@@ -572,7 +566,8 @@ attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
if (xml == NULL) {
crm_debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
- pcmk__ipc_send_ack(client, id, flags, "ack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL,
+ CRM_EX_PROTOCOL);
return 0;
} else {
@@ -589,7 +584,7 @@ attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
CRM_ASSERT(client->user != NULL);
pcmk__update_acl_user(xml, PCMK__XA_ATTR_USER, client->user);
- request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK);
+ request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK);
CRM_CHECK(request.op != NULL, return 0);
attrd_handle_request(&request);
diff --git a/daemons/attrd/attrd_messages.c b/daemons/attrd/attrd_messages.c
index 89da6d8..edb33a5 100644
--- a/daemons/attrd/attrd_messages.c
+++ b/daemons/attrd/attrd_messages.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.
*
@@ -12,7 +12,8 @@
#include <glib.h>
#include <crm/common/messages_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/cluster/internal.h> // pcmk__get_node()
+#include <crm/common/xml.h>
#include "pacemaker-attrd.h"
@@ -30,7 +31,7 @@ static int
remove_sync_point_attribute(xmlNode *xml, void *data)
{
pcmk__xe_remove_matching_attrs(xml, is_sync_point_attr, NULL);
- pcmk__xe_foreach_child(xml, XML_ATTR_OP, remove_sync_point_attribute, NULL);
+ pcmk__xe_foreach_child(xml, PCMK_XE_OP, remove_sync_point_attribute, NULL);
return pcmk_rc_ok;
}
@@ -105,7 +106,8 @@ handle_confirm_request(pcmk__request_t *request)
crm_debug("Received confirmation from %s", request->peer);
- if (crm_element_value_int(request->xml, XML_LRM_ATTR_CALLID, &callid) == -1) {
+ if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID,
+ &callid) == -1) {
pcmk__set_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
"Could not get callid from XML");
} else {
@@ -147,8 +149,14 @@ static xmlNode *
handle_remove_request(pcmk__request_t *request)
{
if (request->peer != NULL) {
- const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_NODE_NAME);
- attrd_peer_remove(host, true, request->peer);
+ const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST);
+ bool reap = false;
+
+ if (pcmk__xe_get_bool_attr(request->xml, PCMK__XA_REAP,
+ &reap) != pcmk_rc_ok) {
+ reap = true; // Default to true for backward compatibility
+ }
+ attrd_peer_remove(host, reap, request->peer);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
return NULL;
} else {
@@ -167,27 +175,14 @@ handle_refresh_request(pcmk__request_t *request)
}
static xmlNode *
-handle_sync_request(pcmk__request_t *request)
-{
- if (request->peer != NULL) {
- crm_node_t *peer = crm_get_peer(0, request->peer);
-
- attrd_peer_sync(peer, request->xml);
- pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
- return NULL;
- } else {
- return handle_unknown_request(request);
- }
-}
-
-static xmlNode *
handle_sync_response_request(pcmk__request_t *request)
{
if (request->ipc_client != NULL) {
return handle_unknown_request(request);
} else {
if (request->peer != NULL) {
- crm_node_t *peer = crm_get_peer(0, request->peer);
+ crm_node_t *peer = pcmk__get_node(0, request->peer, NULL,
+ pcmk__node_search_cluster_member);
bool peer_won = attrd_check_for_new_writer(peer, request->xml);
if (!pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
@@ -204,8 +199,9 @@ static xmlNode *
handle_update_request(pcmk__request_t *request)
{
if (request->peer != NULL) {
- const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_NODE_NAME);
- crm_node_t *peer = crm_get_peer(0, request->peer);
+ const char *host = crm_element_value(request->xml, PCMK__XA_ATTR_HOST);
+ crm_node_t *peer = pcmk__get_node(0, request->peer, NULL,
+ pcmk__node_search_cluster_member);
attrd_peer_update(peer, request->xml, host, false);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
@@ -251,7 +247,6 @@ attrd_register_handlers(void)
{ PCMK__ATTRD_CMD_PEER_REMOVE, handle_remove_request },
{ PCMK__ATTRD_CMD_QUERY, handle_query_request },
{ PCMK__ATTRD_CMD_REFRESH, handle_refresh_request },
- { PCMK__ATTRD_CMD_SYNC, handle_sync_request },
{ PCMK__ATTRD_CMD_SYNC_RESPONSE, handle_sync_response_request },
{ PCMK__ATTRD_CMD_UPDATE, handle_update_request },
{ PCMK__ATTRD_CMD_UPDATE_DELAY, handle_update_request },
@@ -323,11 +318,11 @@ attrd_handle_request(pcmk__request_t *request)
void
attrd_broadcast_protocol(void)
{
- 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_xml_add(attrd_op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
+ crm_xml_add(attrd_op, PCMK__XA_T, PCMK__VALUE_ATTRD);
+ crm_xml_add(attrd_op, PCMK__XA_SRC, crm_system_name);
+ crm_xml_add(attrd_op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
crm_xml_add(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL);
crm_xml_add(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION);
crm_xml_add_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1);
@@ -344,9 +339,9 @@ attrd_broadcast_protocol(void)
gboolean
attrd_send_message(crm_node_t *node, xmlNode *data, bool confirm)
{
- const char *op = crm_element_value(data, PCMK__XA_TASK);
+ const char *op = crm_element_value(data, PCMK_XA_TASK);
- crm_xml_add(data, F_TYPE, T_ATTRD);
+ crm_xml_add(data, PCMK__XA_T, PCMK__VALUE_ATTRD);
crm_xml_add(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
/* Request a confirmation from the destination peer node (which could
@@ -358,5 +353,5 @@ attrd_send_message(crm_node_t *node, xmlNode *data, bool confirm)
}
attrd_xml_add_writer(data);
- return send_cluster_message(node, crm_msg_attrd, data, TRUE);
+ return pcmk__cluster_send_message(node, crm_msg_attrd, data);
}
diff --git a/daemons/attrd/attrd_sync.c b/daemons/attrd/attrd_sync.c
index 1a6c24c..de99db2 100644
--- a/daemons/attrd/attrd_sync.c
+++ b/daemons/attrd/attrd_sync.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.
*
@@ -9,8 +9,8 @@
#include <crm_internal.h>
-#include <crm/msg_xml.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/xml.h>
+#include <crm/common/attrs_internal.h>
#include "pacemaker-attrd.h"
@@ -113,7 +113,7 @@ sync_point_str(enum attrd_sync_point sync_point)
} else if (sync_point == attrd_sync_point_cluster) {
return PCMK__VALUE_CLUSTER;
} else {
- return "unknown";
+ return PCMK_VALUE_UNKNOWN;
}
}
@@ -145,13 +145,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request)
waitlist = pcmk__intkey_table(free_waitlist_node);
}
- wl = calloc(sizeof(struct waitlist_node), 1);
-
- CRM_ASSERT(wl != NULL);
-
- wl->client_id = strdup(request->ipc_client->id);
-
- CRM_ASSERT(wl->client_id);
+ wl = pcmk__assert_alloc(1, sizeof(struct waitlist_node));
if (pcmk__str_eq(sync_point, PCMK__VALUE_LOCAL, pcmk__str_none)) {
wl->sync_point = attrd_sync_point_local;
@@ -162,6 +156,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request)
return;
}
+ wl->client_id = pcmk__str_copy(request->ipc_client->id);
wl->ipc_id = request->ipc_id;
wl->flags = request->flags;
@@ -175,7 +170,7 @@ attrd_add_client_to_waitlist(pcmk__request_t *request)
/* And then add the key to the request XML so we can uniquely identify
* it when it comes time to issue the ACK.
*/
- crm_xml_add_int(request->xml, XML_LRM_ATTR_CALLID, waitlist_client);
+ crm_xml_add_int(request->xml, PCMK__XA_CALL_ID, waitlist_client);
}
/*!
@@ -245,7 +240,7 @@ attrd_ack_waitlist_clients(enum attrd_sync_point sync_point, const xmlNode *xml)
return;
}
- if (crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid) == -1) {
+ if (crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid) == -1) {
crm_warn("Could not get callid from request XML");
return;
}
@@ -316,7 +311,8 @@ attrd_request_sync_point(xmlNode *xml)
CRM_CHECK(xml != NULL, return NULL);
if (xml->children != NULL) {
- xmlNode *child = pcmk__xe_match(xml, XML_ATTR_OP, PCMK__XA_ATTR_SYNC_POINT, NULL);
+ xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP,
+ PCMK__XA_ATTR_SYNC_POINT, NULL);
if (child) {
return crm_element_value(child, PCMK__XA_ATTR_SYNC_POINT);
@@ -381,8 +377,10 @@ confirmation_timeout_cb(gpointer data)
}
crm_trace("Timed out waiting for confirmations for client %s", client->id);
- pcmk__ipc_send_ack(client, action->ipc_id, action->flags | crm_ipc_client_response,
- "ack", ATTRD_PROTOCOL_VERSION, CRM_EX_TIMEOUT);
+ pcmk__ipc_send_ack(client, action->ipc_id,
+ action->flags|crm_ipc_client_response,
+ PCMK__XE_ACK, ATTRD_PROTOCOL_VERSION,
+ CRM_EX_TIMEOUT);
g_hash_table_iter_remove(&iter);
crm_trace("%d requests now in expected confirmations table", g_hash_table_size(expected_confirmations));
@@ -486,7 +484,7 @@ attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_f
expected_confirmations = pcmk__intkey_table((GDestroyNotify) free_action);
}
- if (crm_element_value_int(request->xml, XML_LRM_ATTR_CALLID, &callid) == -1) {
+ if (crm_element_value_int(request->xml, PCMK__XA_CALL_ID, &callid) == -1) {
crm_err("Could not get callid from xml");
return;
}
@@ -499,23 +497,17 @@ attrd_expect_confirmations(pcmk__request_t *request, attrd_confirmation_action_f
g_hash_table_iter_init(&iter, peer_protocol_vers);
while (g_hash_table_iter_next(&iter, &host, &ver)) {
if (ATTRD_SUPPORTS_CONFIRMATION(GPOINTER_TO_INT(ver))) {
- char *s = strdup((char *) host);
-
- CRM_ASSERT(s != NULL);
- respondents = g_list_prepend(respondents, s);
+ respondents = g_list_prepend(respondents,
+ pcmk__str_copy((char *) host));
}
}
- action = calloc(1, sizeof(struct confirmation_action));
- CRM_ASSERT(action != NULL);
+ action = pcmk__assert_alloc(1, sizeof(struct confirmation_action));
action->respondents = respondents;
action->fn = fn;
- action->xml = copy_xml(request->xml);
-
- action->client_id = strdup(request->ipc_client->id);
- CRM_ASSERT(action->client_id != NULL);
-
+ action->xml = pcmk__xml_copy(NULL, request->xml);
+ action->client_id = pcmk__str_copy(request->ipc_client->id);
action->ipc_id = request->ipc_id;
action->flags = request->flags;
diff --git a/daemons/attrd/attrd_utils.c b/daemons/attrd/attrd_utils.c
index 341ee1a..2d0bc76 100644
--- a/daemons/attrd/attrd_utils.c
+++ b/daemons/attrd/attrd_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.
*
@@ -19,7 +19,7 @@
#include <crm/crm.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include "pacemaker-attrd.h"
@@ -58,11 +58,12 @@ attrd_clear_requesting_shutdown(void)
* \internal
* \brief Check whether local attribute manager is shutting down
*
- * \param[in] if_requested Also consider presence of "shutdown" attribute
+ * \param[in] if_requested If \c true, also consider presence of
+ * \c PCMK__NODE_ATTR_SHUTDOWN attribute
*
* \return \c true if local attribute manager has begun shutdown sequence
* or (if \p if_requested is \c true) whether local node has a nonzero
- * "shutdown" attribute set, otherwise \c false
+ * \c PCMK__NODE_ATTR_SHUTDOWN attribute set, otherwise \c false
* \note Most callers should pass \c false for \p if_requested, because the
* attribute manager needs to continue performing while the controller is
* shutting down, and even needs to be eligible for election in case all
@@ -175,8 +176,8 @@ attrd_expand_value(const char *value, const char *old_value)
}
int_value += offset;
- if (int_value > INFINITY) {
- int_value = INFINITY;
+ if (int_value > PCMK_SCORE_INFINITY) {
+ int_value = PCMK_SCORE_INFINITY;
}
return int_value;
}
@@ -204,7 +205,7 @@ attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
/* Create a pattern that matches desired attributes */
if (rsc == NULL) {
- pattern = strdup(ATTRD_RE_CLEAR_ALL);
+ pattern = pcmk__str_copy(ATTRD_RE_CLEAR_ALL);
} else if (op == NULL) {
pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
} else {
@@ -238,7 +239,6 @@ attrd_free_attribute(gpointer data)
free(a->id);
free(a->set_id);
free(a->set_type);
- free(a->uuid);
free(a->user);
mainloop_timer_del(a->timer);
@@ -288,11 +288,9 @@ attrd_update_minimum_protocol_ver(const char *host, const char *value)
pcmk__scan_min_int(value, &ver, 0);
if (ver > 0) {
- char *host_name = strdup(host);
-
/* Record the peer attrd's protocol version. */
- CRM_ASSERT(host_name != NULL);
- g_hash_table_insert(peer_protocol_vers, host_name, GINT_TO_POINTER(ver));
+ g_hash_table_insert(peer_protocol_vers, pcmk__str_copy(host),
+ GINT_TO_POINTER(ver));
/* If the protocol version is a new minimum, record it as such. */
if (minimum_protocol_version == -1 || ver < minimum_protocol_version) {
@@ -302,24 +300,3 @@ attrd_update_minimum_protocol_ver(const char *host, const char *value)
}
}
}
-
-void
-attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest)
-{
- /* Copy attributes from the wrapper parent node into the child node.
- * We can't just use copy_in_properties because we want to skip any
- * attributes that are already set on the child. For instance, if
- * we were told to use a specific node, there will already be a node
- * attribute on the child. Copying the parent's node attribute over
- * could result in the wrong value.
- */
- 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 = ((a == NULL) || (a->children == NULL)) ? NULL :
- (const char *) a->children->content;
-
- if (crm_element_value(dest, p_name) == NULL) {
- crm_xml_add(dest, p_name, p_value);
- }
- }
-}
diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c
index 8091c5b..4ae5c8a 100644
--- a/daemons/attrd/pacemaker-attrd.c
+++ b/daemons/attrd/pacemaker-attrd.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.
*
@@ -20,8 +20,6 @@
#include <fcntl.h>
#include <crm/crm.h>
-#include <crm/cib/internal.h>
-#include <crm/msg_xml.h>
#include <crm/pengine/rules.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/iso8601.h>
@@ -31,7 +29,7 @@
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include "pacemaker-attrd.h"
#define SUMMARY "daemon for managing Pacemaker node attributes"
@@ -59,7 +57,7 @@ static pcmk__supported_format_t formats[] = {
};
lrmd_t *the_lrmd = NULL;
-crm_cluster_t *attrd_cluster = NULL;
+pcmk_cluster_t *attrd_cluster = NULL;
crm_trigger_t *attrd_config_read = NULL;
crm_exit_t attrd_exit_status = CRM_EX_OK;
@@ -136,7 +134,7 @@ main(int argc, char **argv)
// Open additional log files
pcmk__add_logfiles(log_files, out);
- crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
+ crm_log_init(PCMK__VALUE_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
crm_notice("Starting Pacemaker node attribute manager%s",
stand_alone ? " in standalone mode" : "");
diff --git a/daemons/attrd/pacemaker-attrd.h b/daemons/attrd/pacemaker-attrd.h
index b8929a7..76faf04 100644
--- a/daemons/attrd/pacemaker-attrd.h
+++ b/daemons/attrd/pacemaker-attrd.h
@@ -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.
*
@@ -16,7 +16,7 @@
#include <crm/cluster.h>
#include <crm/cluster/election_internal.h>
#include <crm/common/messages_internal.h>
-#include <crm/cib/internal.h>
+#include <crm/cib/cib_types.h>
/*
* Legacy attrd (all pre-1.1.11 Pacemaker versions, plus all versions when used
@@ -31,9 +31,8 @@
* -------- --------- -------------------
* 1 1.1.11 PCMK__ATTRD_CMD_UPDATE (PCMK__XA_ATTR_NAME only),
* PCMK__ATTRD_CMD_PEER_REMOVE, PCMK__ATTRD_CMD_REFRESH,
- * PCMK__ATTRD_CMD_FLUSH, PCMK__ATTRD_CMD_SYNC,
- * PCMK__ATTRD_CMD_SYNC_RESPONSE
- * 1 1.1.13 PCMK__ATTRD_CMD_UPDATE (with PCMK__XA_ATTR_PATTERN),
+ * PCMK__ATTRD_CMD_FLUSH, PCMK__ATTRD_CMD_SYNC_RESPONSE
+ * 1 1.1.13 PCMK__ATTRD_CMD_UPDATE (with PCMK__XA_ATTR_REGEX),
* PCMK__ATTRD_CMD_QUERY
* 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH,
* PCMK__ATTRD_CMD_UPDATE_DELAY
@@ -42,14 +41,16 @@
* 4 2.1.5 Multiple attributes can be updated in a single IPC
* message
* 5 2.1.5 Peers can request confirmation of a sent message
+ * 6 2.1.7 PCMK__ATTRD_CMD_PEER_REMOVE supports PCMK__XA_REAP
*/
-#define ATTRD_PROTOCOL_VERSION "5"
+#define ATTRD_PROTOCOL_VERSION "6"
#define ATTRD_SUPPORTS_MULTI_MESSAGE(x) ((x) >= 4)
#define ATTRD_SUPPORTS_CONFIRMATION(x) ((x) >= 5)
-#define attrd_send_ack(client, id, flags) \
- pcmk__ipc_send_ack((client), (id), (flags), "ack", ATTRD_PROTOCOL_VERSION, CRM_EX_INDETERMINATE)
+#define attrd_send_ack(client, id, flags) \
+ pcmk__ipc_send_ack((client), (id), (flags), PCMK__XE_ACK, \
+ ATTRD_PROTOCOL_VERSION, CRM_EX_INDETERMINATE)
void attrd_init_mainloop(void);
void attrd_run_mainloop(void);
@@ -65,6 +66,7 @@ void attrd_ipc_fini(void);
int attrd_cib_connect(int max_retry);
void attrd_cib_disconnect(void);
void attrd_cib_init(void);
+void attrd_cib_erase_transient_attrs(const char *node);
bool attrd_value_needs_expansion(const char *value);
int attrd_expand_value(const char *value, const char *old_value);
@@ -116,47 +118,75 @@ void attrd_declare_winner(void);
void attrd_remove_voter(const crm_node_t *peer);
void attrd_xml_add_writer(xmlNode *xml);
-typedef struct attribute_s {
- char *uuid; /* TODO: Remove if at all possible */
- char *id;
- char *set_id;
- char *set_type;
- GHashTable *values;
- int update;
- int timeout_ms;
-
- /* TODO: refactor these three as a bitmask */
- bool changed; /* whether attribute value has changed since last write */
- bool unknown_peer_uuids; /* whether we know we're missing a peer uuid */
- gboolean is_private; /* whether to keep this attribute out of the CIB */
-
- mainloop_timer_t *timer;
-
- char *user;
-
- gboolean force_write; /* Flag for updating attribute by ignoring delay */
+enum attrd_attr_flags {
+ attrd_attr_none = 0U,
+ attrd_attr_changed = (1U << 0), // Attribute value has changed since last write
+ attrd_attr_uuid_missing = (1U << 1), // Whether we know we're missing a peer UUID
+ attrd_attr_is_private = (1U << 2), // Whether to keep this attribute out of the CIB
+ attrd_attr_force_write = (1U << 3), // Update attribute by ignoring delay
+};
+typedef struct attribute_s {
+ char *id; // Attribute name
+ char *set_type; // PCMK_XE_INSTANCE_ATTRIBUTES or PCMK_XE_UTILIZATION
+ char *set_id; // Set's XML ID to use when writing
+ char *user; // ACL user to use for CIB writes
+ int update; // Call ID of pending write
+ int timeout_ms; // How long to wait for more changes before writing
+ uint32_t flags; // Group of enum attrd_attr_flags
+ GHashTable *values; // Key: node name, value: attribute_value_t
+ mainloop_timer_t *timer; // Timer to use for timeout_ms
} attribute_t;
+#define attrd_set_attr_flags(attr, flags_to_set) do { \
+ (attr)->flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Value for attribute", (attr)->id, \
+ (attr)->flags, (flags_to_set), #flags_to_set); \
+ } while (0)
+
+#define attrd_clear_attr_flags(attr, flags_to_clear) do { \
+ (attr)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Value for attribute", (attr)->id, \
+ (attr)->flags, (flags_to_clear), #flags_to_clear); \
+ } while (0)
+
+enum attrd_value_flags {
+ attrd_value_none = 0U,
+ attrd_value_remote = (1U << 0), // Value is for Pacemaker Remote node
+ attrd_value_from_peer = (1U << 1), // Value is from peer sync response
+};
+
typedef struct attribute_value_s {
- uint32_t nodeid;
- gboolean is_remote;
- char *nodename;
- char *current;
- char *requested;
- gboolean seen;
+ char *nodename; // Node that this value is for
+ char *current; // Attribute value
+ char *requested; // Value specified in pending CIB write, if any
+ uint32_t nodeid; // Cluster node ID of node that this value is for
+ uint32_t flags; // Group of attrd_value_flags
} attribute_value_t;
-extern crm_cluster_t *attrd_cluster;
+#define attrd_set_value_flags(attr_value, flags_to_set) do { \
+ (attr_value)->flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Value for node", (attr_value)->nodename, \
+ (attr_value)->flags, (flags_to_set), #flags_to_set); \
+ } while (0)
+
+#define attrd_clear_value_flags(attr_value, flags_to_clear) do { \
+ (attr_value)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Value for node", (attr_value)->nodename, \
+ (attr_value)->flags, (flags_to_clear), #flags_to_clear); \
+ } while (0)
+
+extern pcmk_cluster_t *attrd_cluster;
extern GHashTable *attributes;
extern GHashTable *peer_protocol_vers;
#define CIB_OP_TIMEOUT_S 120
int attrd_cluster_connect(void);
+void attrd_broadcast_value(const attribute_t *a, const attribute_value_t *v);
void attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
bool filter);
-void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
+void attrd_peer_sync(crm_node_t *peer);
void attrd_peer_remove(const char *host, bool uncache, const char *source);
void attrd_peer_clear_failure(pcmk__request_t *request);
void attrd_peer_sync_response(const crm_node_t *peer, bool peer_won,
@@ -176,6 +206,8 @@ void attrd_clear_value_seen(void);
void attrd_free_attribute(gpointer data);
void attrd_free_attribute_value(gpointer data);
attribute_t *attrd_populate_attribute(xmlNode *xml, const char *attr);
+char *attrd_set_id(const attribute_t *attr, const char *node_state_id);
+char *attrd_nvpair_id(const attribute_t *attr, const char *node_state_id);
enum attrd_write_options {
attrd_write_changed = 0,
@@ -214,8 +246,6 @@ void attrd_remove_client_from_waitlist(pcmk__client_t *client);
const char *attrd_request_sync_point(xmlNode *xml);
bool attrd_request_has_sync_point(xmlNode *xml);
-void attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest);
-
extern gboolean stand_alone;
#endif /* PACEMAKER_ATTRD__H */
diff --git a/daemons/based/Makefile.am b/daemons/based/Makefile.am
index 022fc47..c10b461 100644
--- a/daemons/based/Makefile.am
+++ b/daemons/based/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,6 +8,7 @@
#
include $(top_srcdir)/mk/common.mk
+include $(top_srcdir)/mk/man.mk
EXTRA_DIST = cib.pam
@@ -35,6 +36,12 @@ pacemaker_based_SOURCES = pacemaker-based.c \
based_remote.c \
based_transaction.c
+if BUILD_XML_HELP
+man7_MANS = pacemaker-based.7
+endif
+
+CLEANFILES = $(man7_MANS)
+
.PHONY: install-exec-hook
install-exec-hook:
if BUILD_LEGACY_LINKS
diff --git a/daemons/based/based_callbacks.c b/daemons/based/based_callbacks.c
index 4fac222..b1a8209 100644
--- a/daemons/based/based_callbacks.c
+++ b/daemons/based/based_callbacks.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.
*
@@ -25,7 +25,6 @@
#include <crm/crm.h>
#include <crm/cib.h>
-#include <crm/msg_xml.h>
#include <crm/cluster/internal.h>
#include <crm/common/xml.h>
@@ -72,11 +71,11 @@ cib_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
if (cib_shutdown_flag) {
crm_info("Ignoring new IPC client [%d] during shutdown",
pcmk__client_pid(c));
- return -EPERM;
+ return -ECONNREFUSED;
}
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -159,20 +158,20 @@ static xmlNode *
create_cib_reply(const char *op, const char *call_id, const char *client_id,
int call_options, int rc, xmlNode *call_data)
{
- xmlNode *reply = create_xml_node(NULL, "cib-reply");
+ xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_CIB_REPLY);
- CRM_ASSERT(reply != NULL);
-
- crm_xml_add(reply, F_TYPE, T_CIB);
- crm_xml_add(reply, F_CIB_OPERATION, op);
- crm_xml_add(reply, F_CIB_CALLID, call_id);
- crm_xml_add(reply, F_CIB_CLIENTID, client_id);
- crm_xml_add_int(reply, F_CIB_CALLOPTS, call_options);
- crm_xml_add_int(reply, F_CIB_RC, rc);
+ crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(reply, PCMK__XA_CIB_OP, op);
+ crm_xml_add(reply, PCMK__XA_CIB_CALLID, call_id);
+ crm_xml_add(reply, PCMK__XA_CIB_CLIENTID, client_id);
+ crm_xml_add_int(reply, PCMK__XA_CIB_CALLOPT, call_options);
+ crm_xml_add_int(reply, PCMK__XA_CIB_RC, rc);
if (call_data != NULL) {
+ xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_CIB_CALLDATA);
+
crm_trace("Attaching reply output");
- add_message_xml(reply, F_CIB_CALLDATA, call_data);
+ pcmk__xml_copy(wrapper, call_data);
}
crm_log_xml_explicit(reply, "cib:reply");
@@ -189,7 +188,7 @@ do_local_notify(const xmlNode *notify_src, const char *client_id,
CRM_ASSERT(notify_src && client_id);
- crm_element_value_int(notify_src, F_CIB_CALLID, &call_id);
+ crm_element_value_int(notify_src, PCMK__XA_CIB_CALLID, &call_id);
client_obj = pcmk__find_client_by_id(client_id);
if (client_obj == NULL) {
@@ -252,10 +251,10 @@ void
cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
pcmk__client_t *cib_client, gboolean privileged)
{
- const char *op = crm_element_value(op_request, F_CIB_OPERATION);
+ const char *op = crm_element_value(op_request, PCMK__XA_CIB_OP);
int call_options = cib_none;
- crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
+ crm_element_value_int(op_request, PCMK__XA_CIB_CALLOPT, &call_options);
/* Requests with cib_transaction set should not be sent to based directly
* (outside of a commit-transaction request)
@@ -266,38 +265,43 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none)) {
if (flags & crm_ipc_client_response) {
- xmlNode *ack = create_xml_node(NULL, __func__);
+ xmlNode *ack = pcmk__xe_create(NULL, __func__);
- crm_xml_add(ack, F_CIB_OPERATION, CRM_OP_REGISTER);
- crm_xml_add(ack, F_CIB_CLIENTID, cib_client->id);
+ crm_xml_add(ack, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
+ crm_xml_add(ack, PCMK__XA_CIB_CLIENTID, cib_client->id);
pcmk__ipc_send_xml(cib_client, id, ack, flags);
cib_client->request_id = 0;
free_xml(ack);
}
return;
- } else if (pcmk__str_eq(op, T_CIB_NOTIFY, pcmk__str_none)) {
+ } else if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) {
/* Update the notify filters for this client */
int on_off = 0;
crm_exit_t status = CRM_EX_OK;
uint64_t bit = UINT64_C(0);
- const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);
+ const char *type = crm_element_value(op_request,
+ PCMK__XA_CIB_NOTIFY_TYPE);
- crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
+ crm_element_value_int(op_request, PCMK__XA_CIB_NOTIFY_ACTIVATE,
+ &on_off);
crm_debug("Setting %s callbacks %s for client %s",
type, (on_off? "on" : "off"), pcmk__client_name(cib_client));
- if (pcmk__str_eq(type, T_CIB_POST_NOTIFY, pcmk__str_casei)) {
+ if (pcmk__str_eq(type, PCMK__VALUE_CIB_POST_NOTIFY, pcmk__str_none)) {
bit = cib_notify_post;
- } else if (pcmk__str_eq(type, T_CIB_PRE_NOTIFY, pcmk__str_casei)) {
+ } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_PRE_NOTIFY,
+ pcmk__str_none)) {
bit = cib_notify_pre;
- } else if (pcmk__str_eq(type, T_CIB_UPDATE_CONFIRM, pcmk__str_casei)) {
+ } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_UPDATE_CONFIRMATION,
+ pcmk__str_none)) {
bit = cib_notify_confirm;
- } else if (pcmk__str_eq(type, T_CIB_DIFF_NOTIFY, pcmk__str_casei)) {
+ } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY,
+ pcmk__str_none)) {
bit = cib_notify_diff;
} else {
@@ -312,7 +316,7 @@ cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode * op_request,
}
}
- pcmk__ipc_send_ack(cib_client, id, flags, "ack", NULL, status);
+ pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_ACK, NULL, status);
return;
}
@@ -329,12 +333,13 @@ cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean
xmlNode *op_request = pcmk__client_data2xml(cib_client, data, &id, &flags);
if (op_request) {
- crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
+ crm_element_value_int(op_request, PCMK__XA_CIB_CALLOPT, &call_options);
}
if (op_request == NULL) {
crm_trace("Invalid message from %p", c);
- pcmk__ipc_send_ack(cib_client, id, flags, "nack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(cib_client, id, flags, PCMK__XE_NACK, NULL,
+ CRM_EX_PROTOCOL);
return 0;
} else if(cib_client == NULL) {
@@ -349,12 +354,13 @@ cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean
}
if (cib_client->name == NULL) {
- const char *value = crm_element_value(op_request, F_CIB_CLIENTNAME);
+ const char *value = crm_element_value(op_request,
+ PCMK__XA_CIB_CLIENTNAME);
if (value == NULL) {
cib_client->name = pcmk__itoa(cib_client->pid);
} else {
- cib_client->name = strdup(value);
+ cib_client->name = pcmk__str_copy(value);
if (crm_is_daemon_name(value)) {
pcmk__set_client_flags(cib_client, cib_is_daemon);
}
@@ -363,7 +369,7 @@ cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean
/* Allow cluster daemons more leeway before being evicted */
if (pcmk_is_set(cib_client->flags, cib_is_daemon)) {
- const char *qmax = cib_config_lookup("cluster-ipc-limit");
+ const char *qmax = cib_config_lookup(PCMK_OPT_CLUSTER_IPC_LIMIT);
if (pcmk__set_client_queue_max(cib_client, qmax)) {
crm_trace("IPC threshold for client %s[%u] is now %u",
@@ -372,11 +378,11 @@ cib_common_callback(qb_ipcs_connection_t * c, void *data, size_t size, gboolean
}
}
- crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
- crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
+ crm_xml_add(op_request, PCMK__XA_CIB_CLIENTID, cib_client->id);
+ crm_xml_add(op_request, PCMK__XA_CIB_CLIENTNAME, cib_client->name);
CRM_LOG_ASSERT(cib_client->user != NULL);
- pcmk__update_acl_user(op_request, F_CIB_USER, cib_client->user);
+ pcmk__update_acl_user(op_request, PCMK__XA_CIB_USER, cib_client->user);
cib_common_callback_worker(id, flags, op_request, cib_client, privileged);
free_xml(op_request);
@@ -393,7 +399,7 @@ cib_digester_cb(gpointer data)
{
if (based_is_primary) {
char buffer[32];
- xmlNode *ping = create_xml_node(NULL, "ping");
+ xmlNode *ping = pcmk__xe_create(NULL, PCMK__XE_PING);
ping_seq++;
free(ping_digest);
@@ -402,12 +408,12 @@ cib_digester_cb(gpointer data)
snprintf(buffer, 32, "%" PRIu64, ping_seq);
crm_trace("Requesting peer digests (%s)", buffer);
- crm_xml_add(ping, F_TYPE, "cib");
- crm_xml_add(ping, F_CIB_OPERATION, CRM_OP_PING);
- crm_xml_add(ping, F_CIB_PING_ID, buffer);
+ crm_xml_add(ping, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(ping, PCMK__XA_CIB_OP, CRM_OP_PING);
+ crm_xml_add(ping, PCMK__XA_CIB_PING_ID, buffer);
- crm_xml_add(ping, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
- send_cluster_message(NULL, crm_msg_cib, ping, TRUE);
+ crm_xml_add(ping, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
+ pcmk__cluster_send_message(NULL, crm_msg_cib, ping);
free_xml(ping);
}
@@ -418,14 +424,17 @@ static void
process_ping_reply(xmlNode *reply)
{
uint64_t seq = 0;
- const char *host = crm_element_value(reply, F_ORIG);
+ const char *host = crm_element_value(reply, PCMK__XA_SRC);
- xmlNode *pong = get_message_xml(reply, F_CIB_CALLDATA);
- const char *seq_s = crm_element_value(pong, F_CIB_PING_ID);
- const char *digest = crm_element_value(pong, XML_ATTR_DIGEST);
+ xmlNode *wrapper = pcmk__xe_first_child(reply, PCMK__XE_CIB_CALLDATA, NULL,
+ NULL);
+ xmlNode *pong = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
+ const char *seq_s = crm_element_value(pong, PCMK__XA_CIB_PING_ID);
+ const char *digest = crm_element_value(pong, PCMK__XA_DIGEST);
if (seq_s == NULL) {
- crm_debug("Ignoring ping reply with no " F_CIB_PING_ID);
+ crm_debug("Ignoring ping reply with no " PCMK__XA_CIB_PING_ID);
return;
} else {
@@ -447,7 +456,7 @@ process_ping_reply(xmlNode *reply)
crm_trace("Ignoring ping reply %s from %s: cib updated since", seq_s, host);
} else {
- const char *version = crm_element_value(pong, XML_ATTR_CRM_VERSION);
+ const char *version = crm_element_value(pong, PCMK_XA_CRM_FEATURE_SET);
if(ping_digest == NULL) {
crm_trace("Calculating new digest");
@@ -456,16 +465,30 @@ process_ping_reply(xmlNode *reply)
crm_trace("Processing ping reply %s from %s (%s)", seq_s, host, digest);
if (!pcmk__str_eq(ping_digest, digest, pcmk__str_casei)) {
- xmlNode *remote_cib = get_message_xml(pong, F_CIB_CALLDATA);
+ xmlNode *wrapper = pcmk__xe_first_child(pong, PCMK__XE_CIB_CALLDATA,
+ NULL, NULL);
+ xmlNode *remote_cib = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
+ const char *admin_epoch_s = NULL;
+ const char *epoch_s = NULL;
+ const char *num_updates_s = NULL;
+
+ if (remote_cib != NULL) {
+ admin_epoch_s = crm_element_value(remote_cib,
+ PCMK_XA_ADMIN_EPOCH);
+ epoch_s = crm_element_value(remote_cib, PCMK_XA_EPOCH);
+ num_updates_s = crm_element_value(remote_cib,
+ PCMK_XA_NUM_UPDATES);
+ }
crm_notice("Local CIB %s.%s.%s.%s differs from %s: %s.%s.%s.%s %p",
- crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN),
- crm_element_value(the_cib, XML_ATTR_GENERATION),
- crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
+ crm_element_value(the_cib, PCMK_XA_ADMIN_EPOCH),
+ crm_element_value(the_cib, PCMK_XA_EPOCH),
+ crm_element_value(the_cib, PCMK_XA_NUM_UPDATES),
ping_digest, host,
- remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION_ADMIN):"_",
- remote_cib?crm_element_value(remote_cib, XML_ATTR_GENERATION):"_",
- remote_cib?crm_element_value(remote_cib, XML_ATTR_NUMUPDATES):"_",
+ pcmk__s(admin_epoch_s, "_"),
+ pcmk__s(epoch_s, "_"),
+ pcmk__s(num_updates_s, "_"),
digest, remote_cib);
if(remote_cib && remote_cib->children) {
@@ -513,10 +536,11 @@ static void
queue_local_notify(xmlNode * notify_src, const char *client_id, gboolean sync_reply,
gboolean from_peer)
{
- cib_local_notify_t *notify = calloc(1, sizeof(cib_local_notify_t));
+ cib_local_notify_t *notify = pcmk__assert_alloc(1,
+ sizeof(cib_local_notify_t));
notify->notify_src = notify_src;
- notify->client_id = strdup(client_id);
+ notify->client_id = pcmk__str_copy(client_id);
notify->sync_reply = sync_reply;
notify->from_peer = from_peer;
@@ -658,12 +682,12 @@ parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request,
const char *op = NULL;
const char *host = NULL;
const char *delegated = NULL;
- const char *originator = crm_element_value(request, F_ORIG);
- const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
+ const char *originator = crm_element_value(request, PCMK__XA_SRC);
+ const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
- if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
+ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
*needs_reply = FALSE;
if (is_reply) {
*local_notify = TRUE;
@@ -675,7 +699,7 @@ parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request,
return TRUE;
}
- op = crm_element_value(request, F_CIB_OPERATION);
+ op = crm_element_value(request, PCMK__XA_CIB_OP);
crm_trace("Processing legacy %s request sent by %s", op, originator);
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_SHUTDOWN, pcmk__str_none)) {
@@ -703,7 +727,7 @@ parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request,
return TRUE;
}
- host = crm_element_value(request, F_CIB_HOST);
+ host = crm_element_value(request, PCMK__XA_CIB_HOST);
if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
crm_trace("Processing %s request sent to us from %s", op, originator);
return TRUE;
@@ -719,7 +743,7 @@ parse_peer_options_v1(const cib__operation_t *operation, xmlNode *request,
return TRUE;
}
- delegated = crm_element_value(request, F_CIB_DELEGATED);
+ delegated = crm_element_value(request, PCMK__XA_CIB_DELEGATED_FROM);
if (delegated != NULL) {
crm_trace("Ignoring message for primary instance");
@@ -755,10 +779,11 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
gboolean *process)
{
const char *host = NULL;
- const char *delegated = crm_element_value(request, F_CIB_DELEGATED);
- const char *op = crm_element_value(request, F_CIB_OPERATION);
- const char *originator = crm_element_value(request, F_ORIG);
- const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
+ const char *delegated = crm_element_value(request,
+ PCMK__XA_CIB_DELEGATED_FROM);
+ const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
+ const char *originator = crm_element_value(request, PCMK__XA_SRC);
+ const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
gboolean is_reply = pcmk__str_eq(reply_to, OUR_NODENAME, pcmk__str_casei);
@@ -767,7 +792,7 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
}
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE, pcmk__str_none)) {
- /* sync_our_cib() sets F_CIB_ISREPLY */
+ // sync_our_cib() sets PCMK__XA_CIB_ISREPLYTO
if (reply_to) {
delegated = reply_to;
}
@@ -783,17 +808,18 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
} else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_UPGRADE, pcmk__str_none)) {
/* Only the DC (node with the oldest software) should process
- * this operation if F_CIB_SCHEMA_MAX is unset
+ * this operation if PCMK__XA_CIB_SCHEMA_MAX is unset.
*
* If the DC is happy it will then send out another
* PCMK__CIB_REQUEST_UPGRADE which will tell all nodes to do the actual
* upgrade.
*
- * Except this time F_CIB_SCHEMA_MAX will be set which puts a
+ * Except this time PCMK__XA_CIB_SCHEMA_MAX will be set which puts a
* limit on how far newer nodes will go
*/
- const char *max = crm_element_value(request, F_CIB_SCHEMA_MAX);
- const char *upgrade_rc = crm_element_value(request, F_CIB_UPGRADE_RC);
+ const char *max = crm_element_value(request, PCMK__XA_CIB_SCHEMA_MAX);
+ const char *upgrade_rc = crm_element_value(request,
+ PCMK__XA_CIB_UPGRADE_RC);
crm_trace("Parsing upgrade %s for %s with max=%s and upgrade_rc=%s",
(is_reply? "reply" : "request"),
@@ -802,7 +828,7 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
if (upgrade_rc != NULL) {
// Our upgrade request was rejected by DC, notify clients of result
- crm_xml_add(request, F_CIB_RC, upgrade_rc);
+ crm_xml_add(request, PCMK__XA_CIB_RC, upgrade_rc);
} else if ((max == NULL) && based_is_primary) {
/* We are the DC, check if this upgrade is allowed */
@@ -817,7 +843,7 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
return FALSE;
}
- } else if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
+ } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
crm_info("Detected legacy %s global update from %s", op, originator);
send_sync_request(NULL);
legacy_mode = TRUE;
@@ -854,7 +880,7 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
*local_notify = pcmk__str_eq(delegated, OUR_NODENAME, pcmk__str_casei);
- host = crm_element_value(request, F_CIB_HOST);
+ host = crm_element_value(request, PCMK__XA_CIB_HOST);
if (pcmk__str_eq(host, OUR_NODENAME, pcmk__str_casei)) {
crm_trace("Processing %s request sent to us from %s", op, originator);
*needs_reply = TRUE;
@@ -871,8 +897,10 @@ parse_peer_options_v2(const cib__operation_t *operation, xmlNode *request,
crm_trace("Processing %s request broadcast by %s call %s on %s "
"(local clients will%s be notified)", op,
- pcmk__s(crm_element_value(request, F_CIB_CLIENTNAME), "client"),
- pcmk__s(crm_element_value(request, F_CIB_CALLID), "without ID"),
+ pcmk__s(crm_element_value(request, PCMK__XA_CIB_CLIENTNAME),
+ "client"),
+ pcmk__s(crm_element_value(request, PCMK__XA_CIB_CALLID),
+ "without ID"),
originator, (*local_notify? "" : "not"));
return TRUE;
}
@@ -904,12 +932,14 @@ parse_peer_options(const cib__operation_t *operation, xmlNode *request,
static void
forward_request(xmlNode *request)
{
- const char *op = crm_element_value(request, F_CIB_OPERATION);
- const char *section = crm_element_value(request, F_CIB_SECTION);
- const char *host = crm_element_value(request, F_CIB_HOST);
- const char *originator = crm_element_value(request, F_ORIG);
- const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
- const char *call_id = crm_element_value(request, F_CIB_CALLID);
+ const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
+ const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
+ const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
+ const char *originator = crm_element_value(request, PCMK__XA_SRC);
+ const char *client_name = crm_element_value(request,
+ PCMK__XA_CIB_CLIENTNAME);
+ const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
+ crm_node_t *peer = NULL;
int log_level = LOG_INFO;
@@ -926,13 +956,15 @@ forward_request(xmlNode *request)
pcmk__s(client_name, "unspecified"),
pcmk__s(call_id, "unspecified"));
- crm_xml_add(request, F_CIB_DELEGATED, OUR_NODENAME);
+ crm_xml_add(request, PCMK__XA_CIB_DELEGATED_FROM, OUR_NODENAME);
- send_cluster_message(((host != NULL)? crm_get_peer(0, host) : NULL),
- crm_msg_cib, request, FALSE);
+ if (host != NULL) {
+ peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
+ }
+ pcmk__cluster_send_message(peer, crm_msg_cib, request);
// Return the request to its original state
- xml_remove_prop(request, F_CIB_DELEGATED);
+ pcmk__xe_remove_attr(request, PCMK__XA_CIB_DELEGATED_FROM);
}
static gboolean
@@ -957,8 +989,10 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb
const char *digest = NULL;
int format = 1;
+ xmlNode *wrapper = NULL;
+
CRM_LOG_ASSERT(result_diff != NULL);
- digest = crm_element_value(result_diff, XML_ATTR_DIGEST);
+ digest = crm_element_value(result_diff, PCMK__XA_DIGEST);
crm_element_value_int(result_diff, PCMK_XA_FORMAT, &format);
cib_diff_version_details(result_diff,
@@ -969,24 +1003,30 @@ send_peer_reply(xmlNode * msg, xmlNode * result_diff, const char *originator, gb
diff_del_admin_epoch, diff_del_epoch, diff_del_updates,
diff_add_admin_epoch, diff_add_epoch, diff_add_updates, digest);
- crm_xml_add(msg, F_CIB_ISREPLY, originator);
- pcmk__xe_set_bool_attr(msg, F_CIB_GLOBAL_UPDATE, true);
- crm_xml_add(msg, F_CIB_OPERATION, PCMK__CIB_REQUEST_APPLY_PATCH);
- crm_xml_add(msg, F_CIB_USER, CRM_DAEMON_USER);
+ crm_xml_add(msg, PCMK__XA_CIB_ISREPLYTO, originator);
+ pcmk__xe_set_bool_attr(msg, PCMK__XA_CIB_UPDATE, true);
+ crm_xml_add(msg, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_APPLY_PATCH);
+ crm_xml_add(msg, PCMK__XA_CIB_USER, CRM_DAEMON_USER);
if (format == 1) {
CRM_ASSERT(digest != NULL);
}
- add_message_xml(msg, F_CIB_UPDATE_DIFF, result_diff);
+ wrapper = pcmk__xe_create(msg, PCMK__XE_CIB_UPDATE_DIFF);
+ pcmk__xml_copy(wrapper, result_diff);
+
crm_log_xml_explicit(msg, "copy");
- return send_cluster_message(NULL, crm_msg_cib, msg, TRUE);
+ return pcmk__cluster_send_message(NULL, crm_msg_cib, msg);
} else if (originator != NULL) {
/* send reply via HA to originating node */
+ const crm_node_t *node =
+ pcmk__get_node(0, originator, NULL,
+ pcmk__node_search_cluster_member);
+
crm_trace("Sending request result to %s only", originator);
- crm_xml_add(msg, F_CIB_ISREPLY, originator);
- return send_cluster_message(crm_get_peer(0, originator), crm_msg_cib, msg, FALSE);
+ crm_xml_add(msg, PCMK__XA_CIB_ISREPLYTO, originator);
+ return pcmk__cluster_send_message(node, crm_msg_cib, msg);
}
return FALSE;
@@ -1020,19 +1060,20 @@ cib_process_request(xmlNode *request, gboolean privileged,
xmlNode *result_diff = NULL;
int rc = pcmk_ok;
- const char *op = crm_element_value(request, F_CIB_OPERATION);
- const char *originator = crm_element_value(request, F_ORIG);
- const char *host = crm_element_value(request, F_CIB_HOST);
+ const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
+ const char *originator = crm_element_value(request, PCMK__XA_SRC);
+ const char *host = crm_element_value(request, PCMK__XA_CIB_HOST);
const char *target = NULL;
- const char *call_id = crm_element_value(request, F_CIB_CALLID);
- const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
- const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
- const char *reply_to = crm_element_value(request, F_CIB_ISREPLY);
+ const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
+ const char *client_id = crm_element_value(request, PCMK__XA_CIB_CLIENTID);
+ const char *client_name = crm_element_value(request,
+ PCMK__XA_CIB_CLIENTNAME);
+ const char *reply_to = crm_element_value(request, PCMK__XA_CIB_ISREPLYTO);
const cib__operation_t *operation = NULL;
cib__op_fn_t op_function = NULL;
- crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
+ crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
if ((host != NULL) && (*host == '\0')) {
host = NULL;
@@ -1053,7 +1094,7 @@ cib_process_request(xmlNode *request, gboolean privileged,
crm_trace("Processing peer %s operation from %s/%s on %s intended for %s (reply=%s)",
op, client_name, call_id, originator, target, reply_to);
} else {
- crm_xml_add(request, F_ORIG, OUR_NODENAME);
+ crm_xml_add(request, PCMK__XA_SRC, OUR_NODENAME);
crm_trace("Processing local %s operation from %s/%s intended for %s", op, client_name, call_id, target);
}
@@ -1124,7 +1165,10 @@ cib_process_request(xmlNode *request, gboolean privileged,
time_t finished = 0;
time_t now = time(NULL);
int level = LOG_INFO;
- const char *section = crm_element_value(request, F_CIB_SECTION);
+ const char *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
+ const char *admin_epoch_s = NULL;
+ const char *epoch_s = NULL;
+ const char *num_updates_s = NULL;
rc = cib_process_command(request, operation, op_function, &op_reply,
&result_diff, privileged);
@@ -1132,7 +1176,7 @@ cib_process_request(xmlNode *request, gboolean privileged,
if (!is_update) {
level = LOG_TRACE;
- } else if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
+ } else if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
switch (rc) {
case pcmk_ok:
level = LOG_INFO;
@@ -1150,13 +1194,19 @@ cib_process_request(xmlNode *request, gboolean privileged,
level = LOG_WARNING;
}
+ if (the_cib != NULL) {
+ admin_epoch_s = crm_element_value(the_cib, PCMK_XA_ADMIN_EPOCH);
+ epoch_s = crm_element_value(the_cib, PCMK_XA_EPOCH);
+ num_updates_s = crm_element_value(the_cib, PCMK_XA_NUM_UPDATES);
+ }
+
do_crm_log(level,
"Completed %s operation for section %s: %s (rc=%d, origin=%s/%s/%s, version=%s.%s.%s)",
op, section ? section : "'all'", pcmk_strerror(rc), rc,
originator ? originator : "local", client_name, call_id,
- the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN) : "0",
- the_cib ? crm_element_value(the_cib, XML_ATTR_GENERATION) : "0",
- the_cib ? crm_element_value(the_cib, XML_ATTR_NUMUPDATES) : "0");
+ pcmk__s(admin_epoch_s, "0"),
+ pcmk__s(epoch_s, "0"),
+ pcmk__s(num_updates_s, "0"));
finished = time(NULL);
if ((finished - now) > 3) {
@@ -1186,7 +1236,8 @@ cib_process_request(xmlNode *request, gboolean privileged,
gboolean broadcast = FALSE;
cib_local_bcast_num++;
- crm_xml_add_int(request, F_CIB_LOCAL_NOTIFY_ID, cib_local_bcast_num);
+ crm_xml_add_int(request, PCMK__XA_CIB_LOCAL_NOTIFY_ID,
+ cib_local_bcast_num);
broadcast = send_peer_reply(request, result_diff, originator, TRUE);
if (broadcast && client_id && local_notify && op_reply) {
@@ -1261,27 +1312,35 @@ static xmlNode *
prepare_input(const xmlNode *request, enum cib__op_type type,
const char **section)
{
+ xmlNode *wrapper = NULL;
xmlNode *input = NULL;
*section = NULL;
switch (type) {
case cib__op_apply_patch:
- if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
- input = get_message_xml(request, F_CIB_UPDATE_DIFF);
- } else {
- input = get_message_xml(request, F_CIB_CALLDATA);
+ {
+ const char *wrapper_name = PCMK__XE_CIB_CALLDATA;
+
+ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
+ wrapper_name = PCMK__XE_CIB_UPDATE_DIFF;
+ }
+ wrapper = pcmk__xe_first_child(request, wrapper_name, NULL,
+ NULL);
+ input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
}
break;
default:
- input = get_message_xml(request, F_CIB_CALLDATA);
- *section = crm_element_value(request, F_CIB_SECTION);
+ wrapper = pcmk__xe_first_child(request, PCMK__XE_CIB_CALLDATA, NULL,
+ NULL);
+ input = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+ *section = crm_element_value(request, PCMK__XA_CIB_SECTION);
break;
}
// Grab the specified section
- if ((*section != NULL) && pcmk__xe_is(input, XML_TAG_CIB)) {
+ if ((*section != NULL) && pcmk__xe_is(input, PCMK_XE_CIB)) {
input = pcmk_find_cib_element(input, *section);
}
@@ -1289,10 +1348,10 @@ prepare_input(const xmlNode *request, enum cib__op_type type,
}
// v1 and v2 patch formats
-#define XPATH_CONFIG_CHANGE \
- "//" XML_CIB_TAG_CRMCONFIG " | " \
- "//" XML_DIFF_CHANGE \
- "[contains(@" XML_DIFF_PATH ",'/" XML_CIB_TAG_CRMCONFIG "/')]"
+#define XPATH_CONFIG_CHANGE \
+ "//" PCMK_XE_CRM_CONFIG " | " \
+ "//" PCMK_XE_CHANGE \
+ "[contains(@" PCMK_XA_PATH ",'/" PCMK_XE_CRM_CONFIG "/')]"
static bool
contains_config_change(xmlNode *diff)
@@ -1323,10 +1382,11 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
const char *op = NULL;
const char *section = NULL;
- const char *call_id = crm_element_value(request, F_CIB_CALLID);
- const char *client_id = crm_element_value(request, F_CIB_CLIENTID);
- const char *client_name = crm_element_value(request, F_CIB_CLIENTNAME);
- const char *originator = crm_element_value(request, F_ORIG);
+ const char *call_id = crm_element_value(request, PCMK__XA_CIB_CALLID);
+ const char *client_id = crm_element_value(request, PCMK__XA_CIB_CLIENTID);
+ const char *client_name = crm_element_value(request,
+ PCMK__XA_CIB_CLIENTNAME);
+ const char *originator = crm_element_value(request, PCMK__XA_SRC);
int rc = pcmk_ok;
@@ -1345,8 +1405,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
*cib_diff = NULL;
/* Start processing the request... */
- op = crm_element_value(request, F_CIB_OPERATION);
- crm_element_value_int(request, F_CIB_CALLOPTS, &call_options);
+ op = crm_element_value(request, PCMK__XA_CIB_OP);
+ crm_element_value_int(request, PCMK__XA_CIB_CALLOPT, &call_options);
if (!privileged && pcmk_is_set(operation->flags, cib__op_attr_privileged)) {
rc = -EACCES;
@@ -1357,7 +1417,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
input = prepare_input(request, operation->type, &section);
if (!pcmk_is_set(operation->flags, cib__op_attr_modifies)) {
- rc = cib_perform_op(op, call_options, op_function, true, section,
+ rc = cib_perform_op(NULL, op, call_options, op_function, true, section,
request, input, false, &config_changed, &the_cib,
&result_cib, NULL, &output);
@@ -1368,11 +1428,11 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
/* @COMPAT: Handle a valid write action (legacy)
*
* @TODO: Re-evaluate whether this is all truly legacy. The cib_force_diff
- * portion is. However, F_CIB_GLOBAL_UPDATE may be set by a sync operation
+ * portion is. However, PCMK__XA_CIB_UPDATE may be set by a sync operation
* even in non-legacy mode, and manage_counters tells xml_create_patchset()
* whether to update version/epoch info.
*/
- if (pcmk__xe_attr_is_true(request, F_CIB_GLOBAL_UPDATE)) {
+ if (pcmk__xe_attr_is_true(request, PCMK__XA_CIB_UPDATE)) {
manage_counters = false;
cib__set_call_options(call_options, "call", cib_force_diff);
crm_trace("Global update detected");
@@ -1390,7 +1450,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
}
// result_cib must not be modified after cib_perform_op() returns
- rc = cib_perform_op(op, call_options, op_function, false, section,
+ rc = cib_perform_op(NULL, op, call_options, op_function, false, section,
request, input, manage_counters, &config_changed,
&the_cib, &result_cib, cib_diff, &output);
@@ -1426,8 +1486,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
}
crm_trace("Activating %s->%s%s",
- crm_element_value(the_cib, XML_ATTR_NUMUPDATES),
- crm_element_value(result_cib, XML_ATTR_NUMUPDATES),
+ crm_element_value(the_cib, PCMK_XA_NUM_UPDATES),
+ crm_element_value(result_cib, PCMK_XA_NUM_UPDATES),
(config_changed? " changed" : ""));
rc = activateCibXml(result_cib, config_changed, op);
@@ -1451,7 +1511,8 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
*/
if ((operation->type == cib__op_commit_transact)
&& pcmk__str_eq(originator, OUR_NODENAME, pcmk__str_casei)
- && compare_version(crm_element_value(the_cib, XML_ATTR_CRM_VERSION),
+ && compare_version(crm_element_value(the_cib,
+ PCMK_XA_CRM_FEATURE_SET),
"3.19.0") < 0) {
sync_our_cib(request, TRUE);
@@ -1473,7 +1534,7 @@ cib_process_command(xmlNode *request, const cib__operation_t *operation,
} else {
crm_trace("Not activating %d %d %s", rc,
pcmk_is_set(call_options, cib_dryrun),
- crm_element_value(result_cib, XML_ATTR_NUMUPDATES));
+ crm_element_value(result_cib, PCMK_XA_NUM_UPDATES));
if (result_cib != the_cib) {
free_xml(result_cib);
@@ -1507,7 +1568,7 @@ void
cib_peer_callback(xmlNode * msg, void *private_data)
{
const char *reason = NULL;
- const char *originator = crm_element_value(msg, F_ORIG);
+ const char *originator = crm_element_value(msg, PCMK__XA_SRC);
if (cib_legacy_mode()
&& pcmk__str_eq(originator, OUR_NODENAME,
@@ -1515,7 +1576,8 @@ cib_peer_callback(xmlNode * msg, void *private_data)
/* message is from ourselves */
int bcast_id = 0;
- if (!(crm_element_value_int(msg, F_CIB_LOCAL_NOTIFY_ID, &bcast_id))) {
+ if (crm_element_value_int(msg, PCMK__XA_CIB_LOCAL_NOTIFY_ID,
+ &bcast_id) == 0) {
check_local_notify(bcast_id);
}
return;
@@ -1525,8 +1587,8 @@ cib_peer_callback(xmlNode * msg, void *private_data)
goto bail;
}
- if (crm_element_value(msg, F_CIB_CLIENTNAME) == NULL) {
- crm_xml_add(msg, F_CIB_CLIENTNAME, originator);
+ if (crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME) == NULL) {
+ crm_xml_add(msg, PCMK__XA_CIB_CLIENTNAME, originator);
}
/* crm_log_xml_trace(msg, "Peer[inbound]"); */
@@ -1535,10 +1597,9 @@ cib_peer_callback(xmlNode * msg, void *private_data)
bail:
if (reason) {
- const char *seq = crm_element_value(msg, F_SEQ);
- const char *op = crm_element_value(msg, F_CIB_OPERATION);
+ const char *op = crm_element_value(msg, PCMK__XA_CIB_OP);
- crm_warn("Discarding %s message (%s) from %s: %s", op, seq, originator, reason);
+ crm_warn("Discarding %s message from %s: %s", op, originator, reason);
}
}
@@ -1565,7 +1626,7 @@ initiate_exit(void)
int active = 0;
xmlNode *leaving = NULL;
- active = crm_active_peers();
+ active = pcmk__cluster_num_active_nodes();
if (active < 2) { // This is the last active node
terminate_cib(__func__, 0);
return;
@@ -1573,11 +1634,11 @@ initiate_exit(void)
crm_info("Sending shutdown request to %d peers", active);
- leaving = create_xml_node(NULL, "exit-notification");
- crm_xml_add(leaving, F_TYPE, "cib");
- crm_xml_add(leaving, F_CIB_OPERATION, PCMK__CIB_REQUEST_SHUTDOWN);
+ leaving = pcmk__xe_create(NULL, PCMK__XE_EXIT_NOTIFICATION);
+ crm_xml_add(leaving, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(leaving, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SHUTDOWN);
- send_cluster_message(NULL, crm_msg_cib, leaving, TRUE);
+ pcmk__cluster_send_message(NULL, crm_msg_cib, leaving);
free_xml(leaving);
g_timeout_add(EXIT_ESCALATION_MS, cib_force_exit, NULL);
@@ -1688,14 +1749,14 @@ terminate_cib(const char *caller, int fast)
* peer caches).
*/
if (fast == 0) {
- crm_cluster_disconnect(crm_cluster);
+ pcmk_cluster_disconnect(crm_cluster);
}
g_main_loop_quit(mainloop);
} else {
/* Quit via clean exit. Even the peer status callback can disconnect
* here, because we're not returning control to the caller. */
- crm_cluster_disconnect(crm_cluster);
+ pcmk_cluster_disconnect(crm_cluster);
pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
crm_exit(CRM_EX_OK);
}
diff --git a/daemons/based/based_io.c b/daemons/based/based_io.c
index f252ac1..7410b03 100644
--- a/daemons/based/based_io.c
+++ b/daemons/based/based_io.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.
*
@@ -29,7 +29,6 @@
#include <crm/cib.h>
#include <crm/common/util.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cib/internal.h>
#include <crm/cluster.h>
@@ -258,20 +257,20 @@ readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
}
- status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE);
+ status = pcmk__xe_first_child(root, PCMK_XE_STATUS, NULL, NULL);
if (discard_status && status != NULL) {
- /* strip out the status section if there is one */
+ // Strip out the PCMK_XE_STATUS section if there is one
free_xml(status);
status = NULL;
}
if (status == NULL) {
- create_xml_node(root, XML_CIB_TAG_STATUS);
+ pcmk__xe_create(root, PCMK_XE_STATUS);
}
/* Do this before schema validation happens */
/* fill in some defaults */
- name = XML_ATTR_GENERATION_ADMIN;
+ name = PCMK_XA_ADMIN_EPOCH;
value = crm_element_value(root, name);
if (value == NULL) {
crm_warn("No value for %s was specified in the configuration.", name);
@@ -283,38 +282,38 @@ readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
crm_xml_add_int(root, name, 0);
}
- name = XML_ATTR_GENERATION;
+ name = PCMK_XA_EPOCH;
value = crm_element_value(root, name);
if (value == NULL) {
crm_xml_add_int(root, name, 0);
}
- name = XML_ATTR_NUMUPDATES;
+ name = PCMK_XA_NUM_UPDATES;
value = crm_element_value(root, name);
if (value == NULL) {
crm_xml_add_int(root, name, 0);
}
// Unset (DC should set appropriate value)
- xml_remove_prop(root, XML_ATTR_DC_UUID);
+ pcmk__xe_remove_attr(root, PCMK_XA_DC_UUID);
if (discard_status) {
crm_log_xml_trace(root, "[on-disk]");
}
- validation = crm_element_value(root, XML_ATTR_VALIDATION);
- if (validate_xml(root, NULL, TRUE) == FALSE) {
+ validation = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
+ if (!pcmk__configured_schema_validates(root)) {
crm_err("CIB does not validate with %s",
pcmk__s(validation, "no schema specified"));
cib_status = -pcmk_err_schema_validation;
+ // @COMPAT Not specifying validate-with is deprecated since 2.1.8
} else if (validation == NULL) {
- int version = 0;
-
- update_validation(&root, &version, 0, FALSE, FALSE);
- if (version > 0) {
+ pcmk__update_schema(&root, NULL, false, false);
+ validation = crm_element_value(root, PCMK_XA_VALIDATE_WITH);
+ if (validation != NULL) {
crm_notice("Enabling %s validation on"
- " the existing (sane) configuration", get_schema_name(version));
+ " the existing (sane) configuration", validation);
} else {
crm_err("CIB does not validate with any known schema");
cib_status = -pcmk_err_schema_validation;
@@ -408,7 +407,7 @@ write_cib_contents(gpointer p)
/* Make a copy of the CIB to write (possibly in a forked child) */
if (p) {
/* Synchronous write out */
- cib_local = copy_xml(p);
+ cib_local = pcmk__xml_copy(NULL, p);
} else {
int pid = 0;
@@ -445,7 +444,7 @@ write_cib_contents(gpointer p)
/* In theory, we can scribble on the_cib here and not affect the parent,
* but let's be safe anyway.
*/
- cib_local = copy_xml(the_cib);
+ cib_local = pcmk__xml_copy(NULL, the_cib);
}
/* Write the CIB */
diff --git a/daemons/based/based_messages.c b/daemons/based/based_messages.c
index 35d639a..87b7eb1 100644
--- a/daemons/based/based_messages.c
+++ b/daemons/based/based_messages.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/ipc_internal.h>
@@ -45,11 +44,11 @@ cib_process_shutdown_req(const char *op, int options, const char *section, xmlNo
xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
xmlNode ** answer)
{
- const char *host = crm_element_value(req, F_ORIG);
+ const char *host = crm_element_value(req, PCMK__XA_SRC);
*answer = NULL;
- if (crm_element_value(req, F_CIB_ISREPLY) == NULL) {
+ if (crm_element_value(req, PCMK__XA_CIB_ISREPLYTO) == NULL) {
crm_info("Peer %s is requesting to shut down", host);
return pcmk_ok;
}
@@ -117,17 +116,21 @@ static int sync_in_progress = 0;
void
send_sync_request(const char *host)
{
- xmlNode *sync_me = create_xml_node(NULL, "sync-me");
+ xmlNode *sync_me = pcmk__xe_create(NULL, "sync-me");
+ crm_node_t *peer = NULL;
crm_info("Requesting re-sync from %s", (host? host : "all peers"));
sync_in_progress = 1;
- crm_xml_add(sync_me, F_TYPE, "cib");
- crm_xml_add(sync_me, F_CIB_OPERATION, PCMK__CIB_REQUEST_SYNC_TO_ONE);
- crm_xml_add(sync_me, F_CIB_DELEGATED,
+ crm_xml_add(sync_me, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(sync_me, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_SYNC_TO_ONE);
+ crm_xml_add(sync_me, PCMK__XA_CIB_DELEGATED_FROM,
stand_alone? "localhost" : crm_cluster->uname);
- send_cluster_message(host ? crm_get_peer(0, host) : NULL, crm_msg_cib, sync_me, FALSE);
+ if (host != NULL) {
+ peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
+ }
+ pcmk__cluster_send_message(peer, crm_msg_cib, sync_me);
free_xml(sync_me);
}
@@ -135,38 +138,44 @@ int
cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
- const char *host = crm_element_value(req, F_ORIG);
- const char *seq = crm_element_value(req, F_CIB_PING_ID);
+ const char *host = crm_element_value(req, PCMK__XA_SRC);
+ const char *seq = crm_element_value(req, PCMK__XA_CIB_PING_ID);
char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
+ xmlNode *wrapper = NULL;
+
crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
- *answer = create_xml_node(NULL, XML_CRM_TAG_PING);
-
- crm_xml_add(*answer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
- crm_xml_add(*answer, XML_ATTR_DIGEST, digest);
- crm_xml_add(*answer, F_CIB_PING_ID, seq);
-
- pcmk__if_tracing(
- {
- // Append additional detail so the receiver can log the differences
- add_message_xml(*answer, F_CIB_CALLDATA, the_cib);
- },
- if (the_cib != NULL) {
- // Always include at least the version details
- xmlNode *shallow = create_xml_node(NULL,
- (const char *) the_cib->name);
-
- copy_in_properties(shallow, the_cib);
- add_message_xml(*answer, F_CIB_CALLDATA, shallow);
- free_xml(shallow);
- }
- );
+ *answer = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
+
+ crm_xml_add(*answer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
+ crm_xml_add(*answer, PCMK__XA_DIGEST, digest);
+ crm_xml_add(*answer, PCMK__XA_CIB_PING_ID, seq);
+
+ wrapper = pcmk__xe_create(*answer, PCMK__XE_CIB_CALLDATA);
+
+ if (the_cib != NULL) {
+ pcmk__if_tracing(
+ {
+ /* Append additional detail so the receiver can log the
+ * differences
+ */
+ pcmk__xml_copy(wrapper, the_cib);
+ },
+ {
+ // Always include at least the version details
+ const char *name = (const char *) the_cib->name;
+ xmlNode *shallow = pcmk__xe_create(wrapper, name);
+
+ pcmk__xe_copy_attrs(shallow, the_cib, pcmk__xaf_none);
+ }
+ );
+ }
crm_info("Reporting our current digest to %s: %s for %s.%s.%s",
host, digest,
- crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN),
- crm_element_value(existing_cib, XML_ATTR_GENERATION),
- crm_element_value(existing_cib, XML_ATTR_NUMUPDATES));
+ crm_element_value(existing_cib, PCMK_XA_ADMIN_EPOCH),
+ crm_element_value(existing_cib, PCMK_XA_EPOCH),
+ crm_element_value(existing_cib, PCMK_XA_NUM_UPDATES));
free(digest);
@@ -188,51 +197,51 @@ cib_process_upgrade_server(const char *op, int options, const char *section, xml
*answer = NULL;
- if(crm_element_value(req, F_CIB_SCHEMA_MAX)) {
+ if (crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX) != NULL) {
/* The originator of an upgrade request sends it to the DC, without
- * F_CIB_SCHEMA_MAX. If an upgrade is needed, the DC re-broadcasts the
- * request with F_CIB_SCHEMA_MAX, and each node performs the upgrade
- * (and notifies its local clients) here.
+ * PCMK__XA_CIB_SCHEMA_MAX. If an upgrade is needed, the DC
+ * re-broadcasts the request with PCMK__XA_CIB_SCHEMA_MAX, and each node
+ * performs the upgrade (and notifies its local clients) here.
*/
return cib_process_upgrade(
op, options, section, req, input, existing_cib, result_cib, answer);
} else {
- int new_version = 0;
- int current_version = 0;
- xmlNode *scratch = copy_xml(existing_cib);
- const char *host = crm_element_value(req, F_ORIG);
- const char *value = crm_element_value(existing_cib, XML_ATTR_VALIDATION);
- const char *client_id = crm_element_value(req, F_CIB_CLIENTID);
- const char *call_opts = crm_element_value(req, F_CIB_CALLOPTS);
- const char *call_id = crm_element_value(req, F_CIB_CALLID);
+ xmlNode *scratch = pcmk__xml_copy(NULL, existing_cib);
+ const char *host = crm_element_value(req, PCMK__XA_SRC);
+ const char *original_schema = NULL;
+ const char *new_schema = NULL;
+ const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
+ const char *call_opts = crm_element_value(req, PCMK__XA_CIB_CALLOPT);
+ const char *call_id = crm_element_value(req, PCMK__XA_CIB_CALLID);
crm_trace("Processing \"%s\" event", op);
- if (value != NULL) {
- current_version = get_schema_version(value);
- }
+ original_schema = crm_element_value(existing_cib,
+ PCMK_XA_VALIDATE_WITH);
+ rc = pcmk__update_schema(&scratch, NULL, true, true);
+ rc = pcmk_rc2legacy(rc);
+ new_schema = crm_element_value(scratch, PCMK_XA_VALIDATE_WITH);
- rc = update_validation(&scratch, &new_version, 0, TRUE, TRUE);
- if (new_version > current_version) {
- xmlNode *up = create_xml_node(NULL, __func__);
+ if (pcmk__cmp_schemas_by_name(new_schema, original_schema) > 0) {
+ xmlNode *up = pcmk__xe_create(NULL, __func__);
rc = pcmk_ok;
crm_notice("Upgrade request from %s verified", host);
- crm_xml_add(up, F_TYPE, "cib");
- crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
- crm_xml_add(up, F_CIB_SCHEMA_MAX, get_schema_name(new_version));
- crm_xml_add(up, F_CIB_DELEGATED, host);
- crm_xml_add(up, F_CIB_CLIENTID, client_id);
- crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
- crm_xml_add(up, F_CIB_CALLID, call_id);
+ crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
+ crm_xml_add(up, PCMK__XA_CIB_SCHEMA_MAX, new_schema);
+ crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
+ crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
+ crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
+ crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
if (cib_legacy_mode() && based_is_primary) {
rc = cib_process_upgrade(
op, options, section, up, input, existing_cib, result_cib, answer);
} else {
- send_cluster_message(NULL, crm_msg_cib, up, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_cib, up);
}
free_xml(up);
@@ -243,25 +252,27 @@ cib_process_upgrade_server(const char *op, int options, const char *section, xml
if (rc != pcmk_ok) {
// Notify originating peer so it can notify its local clients
- crm_node_t *origin = pcmk__search_cluster_node_cache(0, host, NULL);
+ crm_node_t *origin = NULL;
+
+ origin = pcmk__search_node_caches(0, host,
+ pcmk__node_search_cluster_member);
crm_info("Rejecting upgrade request from %s: %s "
CRM_XS " rc=%d peer=%s", host, pcmk_strerror(rc), rc,
(origin? origin->uname : "lost"));
if (origin) {
- xmlNode *up = create_xml_node(NULL, __func__);
-
- crm_xml_add(up, F_TYPE, "cib");
- crm_xml_add(up, F_CIB_OPERATION, PCMK__CIB_REQUEST_UPGRADE);
- crm_xml_add(up, F_CIB_DELEGATED, host);
- crm_xml_add(up, F_CIB_ISREPLY, host);
- crm_xml_add(up, F_CIB_CLIENTID, client_id);
- crm_xml_add(up, F_CIB_CALLOPTS, call_opts);
- crm_xml_add(up, F_CIB_CALLID, call_id);
- crm_xml_add_int(up, F_CIB_UPGRADE_RC, rc);
- if (send_cluster_message(origin, crm_msg_cib, up, TRUE)
- == FALSE) {
+ xmlNode *up = pcmk__xe_create(NULL, __func__);
+
+ crm_xml_add(up, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(up, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_UPGRADE);
+ crm_xml_add(up, PCMK__XA_CIB_DELEGATED_FROM, host);
+ crm_xml_add(up, PCMK__XA_CIB_ISREPLYTO, host);
+ crm_xml_add(up, PCMK__XA_CIB_CLIENTID, client_id);
+ crm_xml_add(up, PCMK__XA_CIB_CALLOPT, call_opts);
+ crm_xml_add(up, PCMK__XA_CIB_CALLID, call_id);
+ crm_xml_add_int(up, PCMK__XA_CIB_UPGRADE_RC, rc);
+ if (!pcmk__cluster_send_message(origin, crm_msg_cib, up)) {
crm_warn("Could not send CIB upgrade result to %s", host);
}
free_xml(up);
@@ -351,7 +362,7 @@ cib_process_replace_svr(const char *op, int options, const char *section, xmlNod
int rc =
cib_process_replace(op, options, section, req, input, existing_cib, result_cib, answer);
- if ((rc == pcmk_ok) && pcmk__xe_is(input, XML_TAG_CIB)) {
+ if ((rc == pcmk_ok) && pcmk__xe_is(input, PCMK_XE_CIB)) {
sync_in_progress = 0;
}
return rc;
@@ -370,32 +381,26 @@ static xmlNode *
cib_msg_copy(xmlNode *msg)
{
static const char *field_list[] = {
- F_XML_TAGNAME,
- F_TYPE,
- F_CIB_CLIENTID,
- F_CIB_CALLOPTS,
- F_CIB_CALLID,
- F_CIB_OPERATION,
- F_CIB_ISREPLY,
- F_CIB_SECTION,
- F_CIB_HOST,
- F_CIB_RC,
- F_CIB_DELEGATED,
- F_CIB_OBJID,
- F_CIB_OBJTYPE,
- F_CIB_EXISTING,
- F_CIB_SEENCOUNT,
- F_CIB_TIMEOUT,
- F_CIB_GLOBAL_UPDATE,
- F_CIB_CLIENTNAME,
- F_CIB_USER,
- F_CIB_NOTIFY_TYPE,
- F_CIB_NOTIFY_ACTIVATE
+ PCMK__XA_T,
+ PCMK__XA_CIB_CLIENTID,
+ PCMK__XA_CIB_CALLOPT,
+ PCMK__XA_CIB_CALLID,
+ PCMK__XA_CIB_OP,
+ PCMK__XA_CIB_ISREPLYTO,
+ PCMK__XA_CIB_SECTION,
+ PCMK__XA_CIB_HOST,
+ PCMK__XA_CIB_RC,
+ PCMK__XA_CIB_DELEGATED_FROM,
+ PCMK__XA_CIB_OBJECT,
+ PCMK__XA_CIB_OBJECT_TYPE,
+ PCMK__XA_CIB_UPDATE,
+ PCMK__XA_CIB_CLIENTNAME,
+ PCMK__XA_CIB_USER,
+ PCMK__XA_CIB_NOTIFY_TYPE,
+ PCMK__XA_CIB_NOTIFY_ACTIVATE,
};
- xmlNode *copy = create_xml_node(NULL, "copy");
-
- CRM_ASSERT(copy != NULL);
+ xmlNode *copy = pcmk__xe_create(NULL, PCMK__XE_COPY);
for (int lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
const char *field = field_list[lpc];
@@ -414,10 +419,11 @@ sync_our_cib(xmlNode * request, gboolean all)
{
int result = pcmk_ok;
char *digest = NULL;
- const char *host = crm_element_value(request, F_ORIG);
- const char *op = crm_element_value(request, F_CIB_OPERATION);
-
+ const char *host = crm_element_value(request, PCMK__XA_SRC);
+ const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
+ crm_node_t *peer = NULL;
xmlNode *replace_request = NULL;
+ xmlNode *wrapper = NULL;
CRM_CHECK(the_cib != NULL, return -EINVAL);
CRM_CHECK(all || (host != NULL), return -EINVAL);
@@ -427,24 +433,30 @@ sync_our_cib(xmlNode * request, gboolean all)
replace_request = cib_msg_copy(request);
if (host != NULL) {
- crm_xml_add(replace_request, F_CIB_ISREPLY, host);
+ crm_xml_add(replace_request, PCMK__XA_CIB_ISREPLYTO, host);
}
if (all) {
- xml_remove_prop(replace_request, F_CIB_HOST);
+ pcmk__xe_remove_attr(replace_request, PCMK__XA_CIB_HOST);
}
- crm_xml_add(replace_request, F_CIB_OPERATION, PCMK__CIB_REQUEST_REPLACE);
- crm_xml_add(replace_request, "original_" F_CIB_OPERATION, op);
- pcmk__xe_set_bool_attr(replace_request, F_CIB_GLOBAL_UPDATE, true);
+ crm_xml_add(replace_request, PCMK__XA_CIB_OP, PCMK__CIB_REQUEST_REPLACE);
+
+ // @TODO Keep for tracing, or drop?
+ crm_xml_add(replace_request, PCMK__XA_ORIGINAL_CIB_OP, op);
+
+ pcmk__xe_set_bool_attr(replace_request, PCMK__XA_CIB_UPDATE, true);
- crm_xml_add(replace_request, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(replace_request, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);
- crm_xml_add(replace_request, XML_ATTR_DIGEST, digest);
+ crm_xml_add(replace_request, PCMK__XA_DIGEST, digest);
- add_message_xml(replace_request, F_CIB_CALLDATA, the_cib);
+ wrapper = pcmk__xe_create(replace_request, PCMK__XE_CIB_CALLDATA);
+ pcmk__xml_copy(wrapper, the_cib);
- if (send_cluster_message
- (all ? NULL : crm_get_peer(0, host), crm_msg_cib, replace_request, FALSE) == FALSE) {
+ if (!all) {
+ peer = pcmk__get_node(0, host, NULL, pcmk__node_search_cluster_member);
+ }
+ if (!pcmk__cluster_send_message(peer, crm_msg_cib, replace_request)) {
result = -ENOTCONN;
}
free_xml(replace_request);
@@ -463,8 +475,8 @@ cib_process_commit_transaction(const char *op, int options, const char *section,
* On failure, our caller will free *result_cib.
*/
int rc = pcmk_rc_ok;
- const char *client_id = crm_element_value(req, F_CIB_CLIENTID);
- const char *origin = crm_element_value(req, F_ORIG);
+ const char *client_id = crm_element_value(req, PCMK__XA_CIB_CLIENTID);
+ const char *origin = crm_element_value(req, PCMK__XA_SRC);
pcmk__client_t *client = pcmk__find_client_by_id(client_id);
rc = based_commit_transaction(input, client, origin, result_cib);
@@ -478,3 +490,49 @@ cib_process_commit_transaction(const char *op, int options, const char *section,
}
return pcmk_rc2legacy(rc);
}
+
+int
+cib_process_schemas(const char *op, int options, const char *section, xmlNode *req,
+ xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib,
+ xmlNode **answer)
+{
+ xmlNode *wrapper = NULL;
+ xmlNode *data = NULL;
+
+ const char *after_ver = NULL;
+ GList *schemas = NULL;
+ GList *already_included = NULL;
+
+ *answer = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS);
+
+ wrapper = pcmk__xe_first_child(req, PCMK__XE_CIB_CALLDATA, NULL, NULL);
+ data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+ if (data == NULL) {
+ crm_warn("No data specified in request");
+ return -EPROTO;
+ }
+
+ after_ver = crm_element_value(data, PCMK_XA_VERSION);
+ if (after_ver == NULL) {
+ crm_warn("No version specified in request");
+ return -EPROTO;
+ }
+
+ /* The client requested all schemas after the latest one we know about, which
+ * means the client is fully up-to-date. Return a properly formatted reply
+ * with no schemas.
+ */
+ if (pcmk__str_eq(after_ver, pcmk__highest_schema_name(), pcmk__str_none)) {
+ return pcmk_ok;
+ }
+
+ schemas = pcmk__schema_files_later_than(after_ver);
+
+ for (GList *iter = schemas; iter != NULL; iter = iter->next) {
+ pcmk__build_schema_xml_node(*answer, iter->data, &already_included);
+ }
+
+ g_list_free_full(schemas, free);
+ g_list_free_full(already_included, free);
+ return pcmk_ok;
+}
diff --git a/daemons/based/based_notify.c b/daemons/based/based_notify.c
index 00a4c54..5160645 100644
--- a/daemons/based/based_notify.c
+++ b/daemons/based/based_notify.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -26,7 +26,6 @@
#include <crm/crm.h>
#include <crm/cib/internal.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/remote_internal.h>
@@ -53,25 +52,27 @@ cib_notify_send_one(gpointer key, gpointer value, gpointer user_data)
return;
}
- type = crm_element_value(update->msg, F_SUBTYPE);
+ type = crm_element_value(update->msg, PCMK__XA_SUBT);
CRM_LOG_ASSERT(type != NULL);
if (pcmk_is_set(client->flags, cib_notify_diff)
- && pcmk__str_eq(type, T_CIB_DIFF_NOTIFY, pcmk__str_casei)) {
+ && pcmk__str_eq(type, PCMK__VALUE_CIB_DIFF_NOTIFY, pcmk__str_none)) {
do_send = TRUE;
} else if (pcmk_is_set(client->flags, cib_notify_confirm)
- && pcmk__str_eq(type, T_CIB_UPDATE_CONFIRM, pcmk__str_casei)) {
+ && pcmk__str_eq(type, PCMK__VALUE_CIB_UPDATE_CONFIRMATION,
+ pcmk__str_none)) {
do_send = TRUE;
} else if (pcmk_is_set(client->flags, cib_notify_pre)
- && pcmk__str_eq(type, T_CIB_PRE_NOTIFY, pcmk__str_casei)) {
+ && pcmk__str_eq(type, PCMK__VALUE_CIB_PRE_NOTIFY,
+ pcmk__str_none)) {
do_send = TRUE;
} else if (pcmk_is_set(client->flags, cib_notify_post)
- && pcmk__str_eq(type, T_CIB_POST_NOTIFY, pcmk__str_casei)) {
-
+ && pcmk__str_eq(type, PCMK__VALUE_CIB_POST_NOTIFY,
+ pcmk__str_none)) {
do_send = TRUE;
}
@@ -125,15 +126,14 @@ cib_notify_send(const xmlNode *xml)
}
static void
-attach_cib_generation(xmlNode *msg, const char *field, xmlNode *a_cib)
+attach_cib_generation(xmlNode *msg)
{
- xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
+ xmlNode *wrapper = pcmk__xe_create(msg, PCMK__XE_CIB_GENERATION);
+ xmlNode *generation = pcmk__xe_create(wrapper, PCMK__XE_GENERATION_TUPLE);
- if (a_cib != NULL) {
- copy_in_properties(generation, a_cib);
+ if (the_cib != NULL) {
+ pcmk__xe_copy_attrs(generation, the_cib, pcmk__xaf_none);
}
- add_message_xml(msg, field, generation);
- free_xml(generation);
}
void
@@ -152,6 +152,7 @@ cib_diff_notify(const char *op, int result, const char *call_id,
uint8_t log_level = LOG_TRACE;
xmlNode *update_msg = NULL;
+ xmlNode *wrapper = NULL;
const char *type = NULL;
if (diff == NULL) {
@@ -191,17 +192,18 @@ cib_diff_notify(const char *op, int result, const char *call_id,
pcmk__s(origin, "unspecified peer"), pcmk_strerror(result));
}
- update_msg = create_xml_node(NULL, "notify");
+ update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
- crm_xml_add(update_msg, F_TYPE, T_CIB_NOTIFY);
- crm_xml_add(update_msg, F_SUBTYPE, T_CIB_DIFF_NOTIFY);
- crm_xml_add(update_msg, F_CIB_OPERATION, op);
- crm_xml_add(update_msg, F_CIB_CLIENTID, client_id);
- crm_xml_add(update_msg, F_CIB_CLIENTNAME, client_name);
- crm_xml_add(update_msg, F_CIB_CALLID, call_id);
- crm_xml_add(update_msg, F_ORIG, origin);
- crm_xml_add_int(update_msg, F_CIB_RC, result);
+ crm_xml_add(update_msg, PCMK__XA_T, PCMK__VALUE_CIB_NOTIFY);
+ crm_xml_add(update_msg, PCMK__XA_SUBT, PCMK__VALUE_CIB_DIFF_NOTIFY);
+ crm_xml_add(update_msg, PCMK__XA_CIB_OP, op);
+ crm_xml_add(update_msg, PCMK__XA_CIB_CLIENTID, client_id);
+ crm_xml_add(update_msg, PCMK__XA_CIB_CLIENTNAME, client_name);
+ crm_xml_add(update_msg, PCMK__XA_CIB_CALLID, call_id);
+ crm_xml_add(update_msg, PCMK__XA_SRC, origin);
+ crm_xml_add_int(update_msg, PCMK__XA_CIB_RC, result);
+ // @COMPAT Unused internally, drop at 3.0.0
if (update != NULL) {
type = (const char *) update->name;
crm_trace("Setting type to update->name: %s", type);
@@ -209,14 +211,20 @@ cib_diff_notify(const char *op, int result, const char *call_id,
type = (const char *) diff->name;
crm_trace("Setting type to new_obj->name: %s", type);
}
- crm_xml_add(update_msg, F_CIB_OBJID, ID(diff));
- crm_xml_add(update_msg, F_CIB_OBJTYPE, type);
- attach_cib_generation(update_msg, "cib_generation", the_cib);
+ // @COMPAT Unused internally, drop at 3.0.0
+ crm_xml_add(update_msg, PCMK__XA_CIB_OBJECT, pcmk__xe_id(diff));
+ crm_xml_add(update_msg, PCMK__XA_CIB_OBJECT_TYPE, type);
+ attach_cib_generation(update_msg);
+
+ // @COMPAT Unused internally, drop at 3.0.0
if (update != NULL) {
- add_message_xml(update_msg, F_CIB_UPDATE, update);
+ wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE);
+ pcmk__xml_copy(wrapper, update);
}
- add_message_xml(update_msg, F_CIB_UPDATE_RESULT, diff);
+
+ wrapper = pcmk__xe_create(update_msg, PCMK__XE_CIB_UPDATE_RESULT);
+ pcmk__xml_copy(wrapper, diff);
crm_log_xml_trace(update_msg, "diff-notify");
cib_notify_send(update_msg);
diff --git a/daemons/based/based_operation.c b/daemons/based/based_operation.c
index 736d425..8dd07af 100644
--- a/daemons/based/based_operation.c
+++ b/daemons/based/based_operation.c
@@ -35,6 +35,7 @@ static const cib__op_fn_t cib_op_functions[] = {
[cib__op_sync_all] = cib_process_sync,
[cib__op_sync_one] = cib_process_sync_one,
[cib__op_upgrade] = cib_process_upgrade_server,
+ [cib__op_schemas] = cib_process_schemas,
};
/*!
diff --git a/daemons/based/based_remote.c b/daemons/based/based_remote.c
index 4aa41fa..b3cb655 100644
--- a/daemons/based/based_remote.c
+++ b/daemons/based/based_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.
*
@@ -27,7 +27,6 @@
#include <glib.h>
#include <libxml/tree.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/xml.h>
@@ -224,20 +223,20 @@ cib_remote_auth(xmlNode * login)
return FALSE;
}
- if (!pcmk__xe_is(login, T_CIB_COMMAND)) {
+ if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) {
crm_err("Unrecognizable message from remote client");
crm_log_xml_info(login, "bad");
return FALSE;
}
- tmp = crm_element_value(login, "op");
+ tmp = crm_element_value(login, PCMK_XA_OP);
if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) {
crm_err("Wrong operation: %s", tmp);
return FALSE;
}
- user = crm_element_value(login, "user");
- pass = crm_element_value(login, "password");
+ user = crm_element_value(login, PCMK_XA_USER);
+ pass = crm_element_value(login, PCMK__XA_PASSWORD);
if (!user || !pass) {
crm_err("missing auth credentials");
@@ -317,7 +316,7 @@ cib_remote_listen(gpointer data)
num_clients++;
new_client = pcmk__new_unauth_client(NULL);
- new_client->remote = calloc(1, sizeof(pcmk__remote_t));
+ new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
if (ssock == remote_tls_fd) {
#ifdef HAVE_GNUTLS_GNUTLS_H
@@ -411,42 +410,35 @@ cib_remote_connection_destroy(gpointer user_data)
static void
cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command)
{
- const char *value = NULL;
-
- if (!pcmk__xe_is(command, T_CIB_COMMAND)) {
+ if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) {
crm_log_xml_trace(command, "bad");
return;
}
if (client->name == NULL) {
- value = crm_element_value(command, F_CLIENTNAME);
- if (value == NULL) {
- client->name = strdup(client->id);
- } else {
- client->name = strdup(value);
- }
+ client->name = pcmk__str_copy(client->id);
}
/* unset dangerous options */
- xml_remove_prop(command, F_ORIG);
- xml_remove_prop(command, F_CIB_HOST);
- xml_remove_prop(command, F_CIB_GLOBAL_UPDATE);
+ pcmk__xe_remove_attr(command, PCMK__XA_SRC);
+ pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST);
+ pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE);
- crm_xml_add(command, F_TYPE, T_CIB);
- crm_xml_add(command, F_CIB_CLIENTID, client->id);
- crm_xml_add(command, F_CIB_CLIENTNAME, client->name);
- crm_xml_add(command, F_CIB_USER, client->user);
+ crm_xml_add(command, PCMK__XA_T, PCMK__VALUE_CIB);
+ crm_xml_add(command, PCMK__XA_CIB_CLIENTID, client->id);
+ crm_xml_add(command, PCMK__XA_CIB_CLIENTNAME, client->name);
+ crm_xml_add(command, PCMK__XA_CIB_USER, client->user);
- if (crm_element_value(command, F_CIB_CALLID) == NULL) {
+ if (crm_element_value(command, PCMK__XA_CIB_CALLID) == NULL) {
char *call_uuid = crm_generate_uuid();
/* fix the command */
- crm_xml_add(command, F_CIB_CALLID, call_uuid);
+ crm_xml_add(command, PCMK__XA_CIB_CALLID, call_uuid);
free(call_uuid);
}
- if (crm_element_value(command, F_CIB_CALLOPTS) == NULL) {
- crm_xml_add_int(command, F_CIB_CALLOPTS, 0);
+ if (crm_element_value(command, PCMK__XA_CIB_CALLOPT) == NULL) {
+ crm_xml_add_int(command, PCMK__XA_CIB_CALLOPT, 0);
}
crm_log_xml_trace(command, "Remote command: ");
@@ -515,17 +507,17 @@ cib_remote_msg(gpointer data)
pcmk__set_client_flags(client, pcmk__client_authenticated);
g_source_remove(client->remote->auth_timeout);
client->remote->auth_timeout = 0;
- client->name = crm_element_value_copy(command, "name");
+ client->name = crm_element_value_copy(command, PCMK_XA_NAME);
- user = crm_element_value(command, "user");
+ user = crm_element_value(command, PCMK_XA_USER);
if (user) {
- client->user = strdup(user);
+ client->user = pcmk__str_copy(user);
}
/* send ACK */
- reg = create_xml_node(NULL, "cib_result");
- crm_xml_add(reg, F_CIB_OPERATION, CRM_OP_REGISTER);
- crm_xml_add(reg, F_CIB_CLIENTID, client->id);
+ reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT);
+ crm_xml_add(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
+ crm_xml_add(reg, PCMK__XA_CIB_CLIENTID, client->id);
pcmk__remote_send_xml(client->remote, reg);
free_xml(reg);
free_xml(command);
@@ -559,8 +551,7 @@ construct_pam_passwd(int num_msg, const struct pam_message **msg,
CRM_CHECK(data, return PAM_CONV_ERR);
CRM_CHECK(num_msg == 1, return PAM_CONV_ERR); /* We only want to handle one message */
- reply = calloc(1, sizeof(struct pam_response));
- CRM_ASSERT(reply != NULL);
+ reply = pcmk__assert_alloc(1, sizeof(struct pam_response));
for (count = 0; count < num_msg; ++count) {
switch (msg[count]->msg_style) {
@@ -634,7 +625,7 @@ authenticate_user(const char *user, const char *passwd)
}
p_conv.conv = construct_pam_passwd;
- p_conv.appdata_ptr = strdup(passwd);
+ p_conv.appdata_ptr = pcmk__str_copy(passwd);
rc = pam_start(pam_name, user, &p_conv, &pam_h);
if (rc != PAM_SUCCESS) {
diff --git a/daemons/based/based_transaction.c b/daemons/based/based_transaction.c
index 89aea2e..39b3439 100644
--- a/daemons/based/based_transaction.c
+++ b/daemons/based/based_transaction.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 the Pacemaker project contributors
+ * Copyright 2023-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -28,21 +28,15 @@
char *
based_transaction_source_str(const pcmk__client_t *client, const char *origin)
{
- char *source = NULL;
-
if (client != NULL) {
- source = crm_strdup_printf("client %s (%s)%s%s",
- pcmk__client_name(client),
- pcmk__s(client->id, "unidentified"),
- ((origin != NULL)? " on " : ""),
- pcmk__s(origin, ""));
-
+ return crm_strdup_printf("client %s (%s)%s%s",
+ pcmk__client_name(client),
+ pcmk__s(client->id, "unidentified"),
+ ((origin != NULL)? " on " : ""),
+ pcmk__s(origin, ""));
} else {
- source = strdup((origin != NULL)? origin : "unknown source");
+ return pcmk__str_copy(pcmk__s(origin, "unknown source"));
}
-
- CRM_ASSERT(source != NULL);
- return source;
}
/*!
@@ -61,11 +55,13 @@ static int
process_transaction_requests(xmlNodePtr transaction,
const pcmk__client_t *client, const char *source)
{
- for (xmlNodePtr 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)) {
- 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);
@@ -127,7 +123,7 @@ based_commit_transaction(xmlNodePtr transaction, const pcmk__client_t *client,
CRM_ASSERT(result_cib != NULL);
- 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 the_cib (created by cib_perform_op()). If
@@ -138,7 +134,7 @@ based_commit_transaction(xmlNodePtr transaction, const pcmk__client_t *client,
* * cib_perform_op() will infer changes for the commit request at the end.
*/
CRM_CHECK((*result_cib != NULL) && (*result_cib != the_cib),
- *result_cib = copy_xml(the_cib));
+ *result_cib = pcmk__xml_copy(NULL, the_cib));
source = based_transaction_source_str(client, origin);
crm_trace("Committing transaction for %s to working CIB", source);
diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c
index 5dd7938..137930b 100644
--- a/daemons/based/pacemaker-based.c
+++ b/daemons/based/pacemaker-based.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,6 @@
#include <crm/crm.h>
#include <crm/cib/internal.h>
-#include <crm/msg_xml.h>
#include <crm/cluster/internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/mainloop.h>
@@ -36,7 +35,7 @@ extern int init_remote_listener(int port, gboolean encrypted);
gboolean cib_shutdown_flag = FALSE;
int cib_status = pcmk_ok;
-crm_cluster_t *crm_cluster = NULL;
+pcmk_cluster_t *crm_cluster = NULL;
GMainLoop *mainloop = NULL;
gchar *cib_root = NULL;
@@ -126,6 +125,19 @@ setup_stand_alone(GError **error)
return pcmk_rc_ok;
}
+/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
+ * crm_attribute --list-options=cluster instead of querying daemon metadata.
+ */
+static int
+based_metadata(pcmk__output_t *out)
+{
+ 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);
+}
+
static GOptionEntry entries[] = {
{ "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone,
"(Advanced use only) Run in stand-alone mode", NULL },
@@ -154,8 +166,7 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
{
GOptionContext *context = NULL;
- context = pcmk__build_arg_context(args, "text (default), xml", group,
- "[metadata]");
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
pcmk__add_main_args(context, entries);
return context;
}
@@ -204,7 +215,13 @@ main(int argc, char **argv)
if ((g_strv_length(processed_args) >= 2)
&& pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
- cib_metadata();
+
+ rc = based_metadata(out);
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Unable to display metadata: %s", pcmk_rc_str(rc));
+ }
goto done;
}
@@ -254,7 +271,7 @@ main(int argc, char **argv)
goto done;
}
- crm_peer_init();
+ pcmk__cluster_init_node_caches();
// Read initial CIB, connect to cluster, and start IPC servers
cib_init();
@@ -267,14 +284,14 @@ main(int argc, char **argv)
/* If main loop returned, clean up and exit. We disconnect in case
* terminate_cib() was called with fast=-1.
*/
- crm_cluster_disconnect(crm_cluster);
+ pcmk_cluster_disconnect(crm_cluster);
pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
done:
g_strfreev(processed_args);
pcmk__free_arg_context(context);
- crm_peer_destroy();
+ pcmk__cluster_destroy_node_caches();
if (local_notify_queue != NULL) {
g_hash_table_destroy(local_notify_queue);
@@ -306,20 +323,19 @@ cib_cs_dispatch(cpg_handle_t handle,
uint32_t kind = 0;
xmlNode *xml = NULL;
const char *from = NULL;
- char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
+ char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
if(data == NULL) {
return;
}
if (kind == crm_class_cluster) {
- xml = string2xml(data);
+ xml = pcmk__xml_parse(data);
if (xml == NULL) {
crm_err("Invalid XML: '%.120s'", data);
free(data);
return;
}
- crm_xml_add(xml, F_ORIG, from);
- /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
+ crm_xml_add(xml, PCMK__XA_SRC, from);
cib_peer_callback(xml, NULL);
}
@@ -359,7 +375,7 @@ cib_peer_update_callback(enum crm_status_type type, crm_node_t * node, const voi
case crm_status_uname:
case crm_status_nstate:
- if (cib_shutdown_flag && (crm_active_peers() < 2)
+ if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2)
&& (pcmk__ipc_client_count() == 0)) {
crm_info("No more peers");
@@ -375,10 +391,10 @@ cib_init(void)
crm_cluster = pcmk_cluster_new();
#if SUPPORT_COROSYNC
- if (is_corosync_cluster()) {
- crm_cluster->destroy = cib_cs_destroy;
- crm_cluster->cpg.cpg_deliver_fn = cib_cs_dispatch;
- crm_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
+ pcmk_cluster_set_destroy_fn(crm_cluster, cib_cs_destroy);
+ pcmk_cpg_set_deliver_fn(crm_cluster, cib_cs_dispatch);
+ pcmk_cpg_set_confchg_fn(crm_cluster, pcmk__cpg_confchg_cb);
}
#endif // SUPPORT_COROSYNC
@@ -390,9 +406,9 @@ cib_init(void)
}
if (!stand_alone) {
- crm_set_status_callback(&cib_peer_update_callback);
+ pcmk__cluster_set_status_callback(&cib_peer_update_callback);
- if (!crm_cluster_connect(crm_cluster)) {
+ if (pcmk_cluster_connect(crm_cluster) != pcmk_rc_ok) {
crm_crit("Cannot sign in to the cluster... terminating");
crm_exit(CRM_EX_FATAL);
}
@@ -419,12 +435,13 @@ startCib(const char *filename)
cib_read_config(config_hash, cib);
- pcmk__scan_port(crm_element_value(cib, "remote-tls-port"), &port);
+ pcmk__scan_port(crm_element_value(cib, PCMK_XA_REMOTE_TLS_PORT), &port);
if (port >= 0) {
remote_tls_fd = init_remote_listener(port, TRUE);
}
- pcmk__scan_port(crm_element_value(cib, "remote-clear-port"), &port);
+ pcmk__scan_port(crm_element_value(cib, PCMK_XA_REMOTE_CLEAR_PORT),
+ &port);
if (port >= 0) {
remote_fd = init_remote_listener(port, FALSE);
}
diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h
index 33c7642..16b0e78 100644
--- a/daemons/based/pacemaker-based.h
+++ b/daemons/based/pacemaker-based.h
@@ -56,7 +56,7 @@ extern crm_trigger_t *cib_writer;
extern gboolean cib_writes_enabled;
extern GMainLoop *mainloop;
-extern crm_cluster_t *crm_cluster;
+extern pcmk_cluster_t *crm_cluster;
extern GHashTable *local_notify_queue;
extern gboolean legacy_mode;
extern gboolean stand_alone;
@@ -122,6 +122,10 @@ int cib_process_commit_transaction(const char *op, int options,
const char *section, xmlNode *req,
xmlNode *input, xmlNode *existing_cib,
xmlNode **result_cib, xmlNode **answer);
+int cib_process_schemas(const char *op, int options, const char *section,
+ xmlNode *req, xmlNode *input, xmlNode *existing_cib,
+ xmlNode **result_cib, xmlNode **answer);
+
void send_sync_request(const char *host);
int sync_our_cib(xmlNode *request, gboolean all);
diff --git a/daemons/controld/controld_attrd.c b/daemons/controld/controld_attrd.c
index 923abb9..f728bef 100644
--- a/daemons/controld/controld_attrd.c
+++ b/daemons/controld/controld_attrd.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2022 the Pacemaker project contributors
+ * Copyright 2006-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,10 +10,10 @@
#include <crm_internal.h>
#include <crm/crm.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_attrd_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <pacemaker-controld.h>
@@ -117,7 +117,7 @@ update_attrd_remote_node_removed(const char *host, const char *user_name)
if (rc == pcmk_rc_ok) {
crm_trace("Asking attribute manager to purge Pacemaker Remote node %s",
host);
- rc = pcmk__attrd_api_purge(attrd_api, host);
+ rc = pcmk__attrd_api_purge(attrd_api, host, true);
}
if (rc != pcmk_rc_ok) {
crm_err("Could not purge Pacemaker Remote node %s "
@@ -136,25 +136,23 @@ update_attrd_clear_failures(const char *host, const char *rsc, const char *op,
rc = pcmk_new_ipc_api(&attrd_api, pcmk_ipc_attrd);
}
if (rc == pcmk_rc_ok) {
- const char *op_desc = pcmk__s(op, "operations");
- const char *interval_desc = "all";
uint32_t attrd_opts = pcmk__node_attr_none;
- if (op != NULL) {
- interval_desc = pcmk__s(interval_spec, "nonrecurring");
- }
if (is_remote_node) {
pcmk__set_node_attr_flags(attrd_opts, pcmk__node_attr_remote);
}
- crm_info("Asking attribute manager to clear failure of %s %s for %s "
- "on %s node %s", interval_desc, op_desc, rsc,
- node_type(is_remote_node), host);
rc = pcmk__attrd_api_clear_failures(attrd_api, host, rsc, op,
interval_spec, NULL, attrd_opts);
}
if (rc != pcmk_rc_ok) {
- crm_err("Could not clear failure attributes for %s on %s node %s%s: %s "
- CRM_XS " rc=%d", pcmk__s(rsc, "all resources"),
- node_type(is_remote_node), host, when(), pcmk_rc_str(rc), rc);
+ const char *interval_desc = "all";
+
+ if (op != NULL) {
+ interval_desc = pcmk__s(interval_spec, "nonrecurring");
+ }
+ crm_err("Could not clear failure of %s %s for %s on %s node %s%s: %s "
+ CRM_XS " rc=%d", interval_desc, pcmk__s(op, "operations"),
+ pcmk__s(rsc, "all resources"), node_type(is_remote_node), host,
+ when(), pcmk_rc_str(rc), rc);
}
}
diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c
index 7078739..16e6424 100644
--- a/daemons/controld/controld_callbacks.c
+++ b/daemons/controld/controld_callbacks.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 <string.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
#include <crm/cib.h>
@@ -27,15 +26,15 @@ void
crmd_ha_msg_filter(xmlNode * msg)
{
if (AM_I_DC) {
- const char *sys_from = crm_element_value(msg, F_CRM_SYS_FROM);
+ const char *sys_from = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
if (pcmk__str_eq(sys_from, CRM_SYSTEM_DC, pcmk__str_casei)) {
- const char *from = crm_element_value(msg, F_ORIG);
+ const char *from = crm_element_value(msg, PCMK__XA_SRC);
if (!pcmk__str_eq(from, controld_globals.our_nodename,
pcmk__str_casei)) {
int level = LOG_INFO;
- const char *op = crm_element_value(msg, F_CRM_TASK);
+ const char *op = crm_element_value(msg, PCMK__XA_CRM_TASK);
/* make sure the election happens NOW */
if (controld_globals.fsa_state != S_ELECTION) {
@@ -53,7 +52,7 @@ crmd_ha_msg_filter(xmlNode * msg)
}
} else {
- const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
+ const char *sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
if (pcmk__str_eq(sys_to, CRM_SYSTEM_DC, pcmk__str_casei)) {
return;
@@ -84,7 +83,7 @@ node_alive(const crm_node_t *node)
// Pacemaker Remote nodes can't be partially alive
return pcmk__str_eq(node->state, CRM_NODE_MEMBER, pcmk__str_casei) ? 1: -1;
- } else if (crm_is_peer_active(node)) {
+ } else if (pcmk__cluster_is_node_active(node)) {
// Completely up cluster node: both cluster member and peer
return 1;
@@ -128,7 +127,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
xmlNode *query = create_request(CRM_OP_HELLO, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
crm_debug("Sending hello to node %u so that it learns our node name", node->id);
- send_cluster_message(node, crm_msg_crmd, query, FALSE);
+ pcmk__cluster_send_message(node, crm_msg_crmd, query);
free_xml(query);
}
@@ -178,7 +177,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
const char *dc_s = controld_globals.dc_name;
if ((dc_s == NULL) && AM_I_DC) {
- dc_s = "true";
+ dc_s = PCMK_VALUE_TRUE;
}
crm_info("Node %s is %s a peer " CRM_XS
@@ -222,7 +221,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
} else if (pcmk__str_eq(node->uname, controld_globals.dc_name,
pcmk__str_casei)
- && !crm_is_peer_active(node)) {
+ && !pcmk__cluster_is_node_active(node)) {
/* Did the DC leave us? */
crm_notice("Our peer on the DC (%s) is dead",
controld_globals.dc_name);
@@ -274,7 +273,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
}
if (down) {
- const char *task = crm_element_value(down->xml, XML_LRM_ATTR_TASK);
+ const char *task = crm_element_value(down->xml, PCMK_XA_OPERATION);
if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) {
@@ -322,8 +321,8 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
crm_update_peer_join(__func__, node, crm_join_none);
check_join_state(controld_globals.fsa_state, __func__);
}
- abort_transition(INFINITY, pcmk__graph_restart, "Node failure",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Node failure", NULL);
fail_incompletable_actions(controld_globals.transition_graph,
node->uuid);
@@ -340,7 +339,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
/* Trigger resource placement on newly integrated nodes */
if (appeared) {
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Pacemaker Remote node integrated", NULL);
}
}
@@ -349,7 +348,8 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
&& (node->when_member > 1)) {
/* The node left CPG but is still a cluster member. Set its
* membership time to 1 to record it in the cluster state as a
- * boolean, so we don't fence it due to node-pending-timeout.
+ * boolean, so we don't fence it due to
+ * PCMK_OPT_NODE_PENDING_TIMEOUT.
*/
node->when_member = 1;
flags |= node_update_cluster;
@@ -361,7 +361,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
if (update == NULL) {
crm_debug("Node state update not yet possible for %s", node->uname);
} else {
- fsa_cib_anon_update(XML_CIB_TAG_STATUS, update);
+ fsa_cib_anon_update(PCMK_XE_STATUS, update);
}
free_xml(update);
}
diff --git a/daemons/controld/controld_cib.c b/daemons/controld/controld_cib.c
index 865e41f..7acff30 100644
--- a/daemons/controld/controld_cib.c
+++ b/daemons/controld/controld_cib.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,6 @@
#include <crm/common/alerts_internal.h>
#include <crm/common/xml.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/lrmd_internal.h>
#include <pacemaker-controld.h>
@@ -59,8 +58,8 @@ do_cib_updated(const char *event, xmlNode * msg)
return;
}
- if (cib__element_in_patchset(patchset, XML_CIB_TAG_ALERTS)
- || cib__element_in_patchset(patchset, XML_CIB_TAG_CRMCONFIG)) {
+ if (cib__element_in_patchset(patchset, PCMK_XE_ALERTS)
+ || cib__element_in_patchset(patchset, PCMK_XE_CRM_CONFIG)) {
controld_trigger_config();
}
@@ -70,21 +69,21 @@ do_cib_updated(const char *event, xmlNode * msg)
return;
}
- client_name = crm_element_value(msg, F_CIB_CLIENTNAME);
+ client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME);
if (!cib__client_triggers_refresh(client_name)) {
// The CIB is still accurate
return;
}
- if (cib__element_in_patchset(patchset, XML_CIB_TAG_NODES)
- || cib__element_in_patchset(patchset, XML_CIB_TAG_STATUS)) {
+ if (cib__element_in_patchset(patchset, PCMK_XE_NODES)
+ || cib__element_in_patchset(patchset, PCMK_XE_STATUS)) {
- /* An unsafe client modified the nodes or status section. Ensure the
- * node list is up-to-date, and start the join process again so we get
- * everyone's current resource history.
+ /* An unsafe client modified the PCMK_XE_NODES or PCMK_XE_STATUS
+ * section. Ensure the node list is up-to-date, and start the join
+ * process again so we get everyone's current resource history.
*/
if (client_name == NULL) {
- client_name = crm_element_value(msg, F_CIB_CLIENTID);
+ client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTID);
}
crm_notice("Populating nodes and starting an election after %s event "
"triggered by %s",
@@ -106,7 +105,7 @@ controld_disconnect_cib_manager(void)
controld_clear_fsa_input_flags(R_CIB_CONNECTED);
- cib_conn->cmds->del_notify_callback(cib_conn, T_CIB_DIFF_NOTIFY,
+ cib_conn->cmds->del_notify_callback(cib_conn, PCMK__VALUE_CIB_DIFF_NOTIFY,
do_cib_updated);
cib_free_callbacks(cib_conn);
@@ -175,7 +174,7 @@ do_cib_control(long long action,
crm_err("Could not set dnotify callback");
} else if (cib_conn->cmds->add_notify_callback(cib_conn,
- T_CIB_DIFF_NOTIFY,
+ PCMK__VALUE_CIB_DIFF_NOTIFY,
update_cb) != pcmk_ok) {
crm_err("Could not set CIB notification callback (update)");
@@ -226,12 +225,9 @@ cib_op_timeout(void)
env_timeout, (env? env : "none"));
}
- calculated_timeout = 1 + crm_active_peers();
- if (crm_remote_peer_cache) {
- calculated_timeout += g_hash_table_size(crm_remote_peer_cache);
- }
- calculated_timeout *= 10;
-
+ calculated_timeout = 10U * (1U
+ + pcmk__cluster_num_active_nodes()
+ + pcmk__cluster_num_remote_nodes());
calculated_timeout = QB_MAX(calculated_timeout, env_timeout);
crm_trace("Calculated timeout: %us", calculated_timeout);
@@ -275,32 +271,32 @@ cib_delete_callback(xmlNode *msg, int call_id, int rc, xmlNode *output,
}
}
-// Searches for various portions of node_state to delete
+// Searches for various portions of PCMK__XE_NODE_STATE to delete
-// Match a particular node's node_state (takes node name 1x)
-#define XPATH_NODE_STATE "//" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']"
+// Match a particular node's PCMK__XE_NODE_STATE (takes node name 1x)
+#define XPATH_NODE_STATE "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']"
// Node's lrm section (name 1x)
-#define XPATH_NODE_LRM XPATH_NODE_STATE "/" XML_CIB_TAG_LRM
+#define XPATH_NODE_LRM XPATH_NODE_STATE "/" PCMK__XE_LRM
-/* Node's lrm_rsc_op entries and lrm_resource entries without unexpired lock
- * (name 2x, (seconds_since_epoch - XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT) 1x)
+/* Node's PCMK__XE_LRM_RSC_OP entries and PCMK__XE_LRM_RESOURCE entries without
+ * unexpired lock
+ * (name 2x, (seconds_since_epoch - PCMK_OPT_SHUTDOWN_LOCK_LIMIT) 1x)
*/
-#define XPATH_NODE_LRM_UNLOCKED XPATH_NODE_STATE "//" XML_LRM_TAG_RSC_OP \
+#define XPATH_NODE_LRM_UNLOCKED XPATH_NODE_STATE "//" PCMK__XE_LRM_RSC_OP \
"|" XPATH_NODE_STATE \
- "//" XML_LRM_TAG_RESOURCE \
- "[not(@" XML_CONFIG_ATTR_SHUTDOWN_LOCK ") " \
- "or " XML_CONFIG_ATTR_SHUTDOWN_LOCK "<%lld]"
+ "//" PCMK__XE_LRM_RESOURCE \
+ "[not(@" PCMK_OPT_SHUTDOWN_LOCK ") " \
+ "or " PCMK_OPT_SHUTDOWN_LOCK "<%lld]"
-// Node's transient_attributes section (name 1x)
-#define XPATH_NODE_ATTRS XPATH_NODE_STATE "/" XML_TAG_TRANSIENT_NODEATTRS
+// Node's PCMK__XE_TRANSIENT_ATTRIBUTES section (name 1x)
+#define XPATH_NODE_ATTRS XPATH_NODE_STATE "/" PCMK__XE_TRANSIENT_ATTRIBUTES
-// Everything under node_state (name 1x)
+// Everything under PCMK__XE_NODE_STATE (name 1x)
#define XPATH_NODE_ALL XPATH_NODE_STATE "/*"
/* Unlocked history + transient attributes
- * (name 2x, (seconds_since_epoch - XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT) 1x,
- * name 1x)
+ * (name 2x, (seconds_since_epoch - PCMK_OPT_SHUTDOWN_LOCK_LIMIT) 1x, name 1x)
*/
#define XPATH_NODE_ALL_UNLOCKED XPATH_NODE_LRM_UNLOCKED "|" XPATH_NODE_ATTRS
@@ -309,7 +305,7 @@ cib_delete_callback(xmlNode *msg, int call_id, int rc, xmlNode *output,
* \brief Get the XPath and description of a node state section to be deleted
*
* \param[in] uname Desired node
- * \param[in] section Subsection of node_state to be deleted
+ * \param[in] section Subsection of \c PCMK__XE_NODE_STATE to be deleted
* \param[out] xpath Where to store XPath of \p section
* \param[out] desc If not \c NULL, where to store description of \p section
*/
@@ -360,10 +356,10 @@ controld_node_state_deletion_strings(const char *uname,
/*!
* \internal
- * \brief Delete subsection of a node's CIB node_state
+ * \brief Delete subsection of a node's CIB \c PCMK__XE_NODE_STATE
*
* \param[in] uname Desired node
- * \param[in] section Subsection of node_state to delete
+ * \param[in] section Subsection of \c PCMK__XE_NODE_STATE to delete
* \param[in] options CIB call options to use
*/
void
@@ -391,12 +387,12 @@ controld_delete_node_state(const char *uname, enum controld_section_e section,
}
// Takes node name and resource ID
-#define XPATH_RESOURCE_HISTORY "//" XML_CIB_TAG_STATE \
- "[@" XML_ATTR_UNAME "='%s']/" \
- XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES \
- "/" XML_LRM_TAG_RESOURCE \
- "[@" XML_ATTR_ID "='%s']"
-// @TODO could add "and @XML_CONFIG_ATTR_SHUTDOWN_LOCK" to limit to locks
+#define XPATH_RESOURCE_HISTORY "//" PCMK__XE_NODE_STATE \
+ "[@" PCMK_XA_UNAME "='%s']/" \
+ PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES \
+ "/" PCMK__XE_LRM_RESOURCE \
+ "[@" PCMK_XA_ID "='%s']"
+// @TODO could add "and @PCMK_OPT_SHUTDOWN_LOCK" to limit to locks
/*!
* \internal
@@ -490,7 +486,7 @@ build_parameter_list(const lrmd_event_data_t *op,
{
GString *list = NULL;
- *result = create_xml_node(NULL, XML_TAG_PARAMS);
+ *result = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
/* Consider all parameters only except private ones to be consistent with
* what scheduler does with calculate_secure_digest().
@@ -547,7 +543,7 @@ build_parameter_list(const lrmd_event_data_t *op,
} else {
crm_trace("Removing attr %s from the xml result", param->rap_name);
- xml_remove_prop(*result, param->rap_name);
+ pcmk__xe_remove_attr(*result, param->rap_name);
}
}
@@ -574,7 +570,9 @@ append_restart_list(lrmd_event_data_t *op, struct ra_metadata_s *metadata,
}
if (pcmk_is_set(metadata->ra_flags, ra_supports_reload_agent)) {
- // Add parameters not marked reloadable to the "op-force-restart" list
+ /* Add parameters not marked reloadable to the PCMK__XA_OP_FORCE_RESTART
+ * list
+ */
list = build_parameter_list(op, metadata, ra_param_reloadable,
&restart);
@@ -583,7 +581,7 @@ append_restart_list(lrmd_event_data_t *op, struct ra_metadata_s *metadata,
*
* Before OCF 1.1, Pacemaker abused "unique=0" to indicate
* reloadability. Add any parameters with unique="1" to the
- * "op-force-restart" list.
+ * PCMK__XA_OP_FORCE_RESTART list.
*/
list = build_parameter_list(op, metadata, ra_param_unique, &restart);
@@ -593,11 +591,13 @@ append_restart_list(lrmd_event_data_t *op, struct ra_metadata_s *metadata,
}
digest = calculate_operation_digest(restart, version);
- /* Add "op-force-restart" and "op-restart-digest" to indicate the resource supports reload,
- * no matter if it actually supports any parameters with unique="1"). */
- crm_xml_add(update, XML_LRM_ATTR_OP_RESTART,
+ /* Add PCMK__XA_OP_FORCE_RESTART and PCMK__XA_OP_RESTART_DIGEST to indicate
+ * the resource supports reload, no matter if it actually supports any
+ * reloadable parameters
+ */
+ crm_xml_add(update, PCMK__XA_OP_FORCE_RESTART,
(list == NULL)? "" : (const char *) list->str);
- crm_xml_add(update, XML_LRM_ATTR_RESTART_DIGEST, digest);
+ crm_xml_add(update, PCMK__XA_OP_RESTART_DIGEST, digest);
if ((list != NULL) && (list->len > 0)) {
crm_trace("%s: %s, %s", op->rsc_id, digest, (const char *) list->str);
@@ -622,17 +622,16 @@ append_secure_list(lrmd_event_data_t *op, struct ra_metadata_s *metadata,
CRM_LOG_ASSERT(op->params != NULL);
- /*
- * To keep XML_LRM_ATTR_OP_SECURE short, we want it to contain the
- * secure parameters but XML_LRM_ATTR_SECURE_DIGEST to be based on
- * the insecure ones
+ /* To keep PCMK__XA_OP_SECURE_PARAMS short, we want it to contain the secure
+ * parameters but PCMK__XA_OP_SECURE_DIGEST to be based on the insecure ones
*/
list = build_parameter_list(op, metadata, ra_param_private, &secure);
if (list != NULL) {
digest = calculate_operation_digest(secure, version);
- crm_xml_add(update, XML_LRM_ATTR_OP_SECURE, (const char *) list->str);
- crm_xml_add(update, XML_LRM_ATTR_SECURE_DIGEST, digest);
+ crm_xml_add(update, PCMK__XA_OP_SECURE_PARAMS,
+ (const char *) list->str);
+ crm_xml_add(update, PCMK__XA_OP_SECURE_DIGEST, digest);
crm_trace("%s: %s, %s", op->rsc_id, digest, (const char *) list->str);
g_string_free(list, TRUE);
@@ -672,7 +671,7 @@ controld_add_resource_history_xml_as(const char *func, xmlNode *parent,
target_rc = rsc_op_expected_rc(op);
- caller_version = g_hash_table_lookup(op->params, XML_ATTR_CRM_VERSION);
+ caller_version = g_hash_table_lookup(op->params, PCMK_XA_CRM_FEATURE_SET);
CRM_CHECK(caller_version != NULL, caller_version = CRM_FEATURE_SET);
xml_op = pcmk__create_history_xml(parent, op, caller_version, target_rc,
@@ -742,8 +741,8 @@ controld_record_pending_op(const char *node_name, const lrmd_rsc_info_t *rsc,
return false;
}
- // Check action's record-pending meta-attribute (defaults to true)
- record_pending = crm_meta_value(op->params, XML_OP_ATTR_PENDING);
+ // Check action's PCMK_META_RECORD_PENDING meta-attribute (defaults to true)
+ record_pending = crm_meta_value(op->params, PCMK_META_RECORD_PENDING);
if ((record_pending != NULL) && !crm_is_true(record_pending)) {
return false;
}
@@ -890,34 +889,34 @@ controld_update_resource_history(const char *node_name,
}
// <status>
- update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ update = pcmk__xe_create(NULL, PCMK_XE_STATUS);
// <node_state ...>
- xml = create_xml_node(update, XML_CIB_TAG_STATE);
+ xml = pcmk__xe_create(update, PCMK__XE_NODE_STATE);
if (pcmk__str_eq(node_name, controld_globals.our_nodename,
pcmk__str_casei)) {
node_id = controld_globals.our_uuid;
} else {
node_id = node_name;
- pcmk__xe_set_bool_attr(xml, XML_NODE_IS_REMOTE, true);
+ pcmk__xe_set_bool_attr(xml, PCMK_XA_REMOTE_NODE, true);
}
- crm_xml_add(xml, XML_ATTR_ID, node_id);
- crm_xml_add(xml, XML_ATTR_UNAME, node_name);
- crm_xml_add(xml, XML_ATTR_ORIGIN, __func__);
+ crm_xml_add(xml, PCMK_XA_ID, node_id);
+ crm_xml_add(xml, PCMK_XA_UNAME, node_name);
+ crm_xml_add(xml, PCMK_XA_CRM_DEBUG_ORIGIN, __func__);
// <lrm ...>
- xml = create_xml_node(xml, XML_CIB_TAG_LRM);
- crm_xml_add(xml, XML_ATTR_ID, node_id);
+ xml = pcmk__xe_create(xml, PCMK__XE_LRM);
+ crm_xml_add(xml, PCMK_XA_ID, node_id);
// <lrm_resources>
- xml = create_xml_node(xml, XML_LRM_TAG_RESOURCES);
+ xml = pcmk__xe_create(xml, PCMK__XE_LRM_RESOURCES);
// <lrm_resource ...>
- xml = create_xml_node(xml, XML_LRM_TAG_RESOURCE);
- crm_xml_add(xml, XML_ATTR_ID, op->rsc_id);
- crm_xml_add(xml, XML_AGENT_ATTR_CLASS, rsc->standard);
- crm_xml_add(xml, XML_AGENT_ATTR_PROVIDER, rsc->provider);
- crm_xml_add(xml, XML_ATTR_TYPE, rsc->type);
+ xml = pcmk__xe_create(xml, PCMK__XE_LRM_RESOURCE);
+ crm_xml_add(xml, PCMK_XA_ID, op->rsc_id);
+ crm_xml_add(xml, PCMK_XA_CLASS, rsc->standard);
+ crm_xml_add(xml, PCMK_XA_PROVIDER, rsc->provider);
+ crm_xml_add(xml, PCMK_XA_TYPE, rsc->type);
if (lock_time != 0) {
/* Actions on a locked resource should either preserve the lock by
* recording it with the action result, or clear it.
@@ -925,16 +924,15 @@ controld_update_resource_history(const char *node_name,
if (!should_preserve_lock(op)) {
lock_time = 0;
}
- crm_xml_add_ll(xml, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
- (long long) lock_time);
+ crm_xml_add_ll(xml, PCMK_OPT_SHUTDOWN_LOCK, (long long) lock_time);
}
if (op->params != NULL) {
container = g_hash_table_lookup(op->params,
- CRM_META "_" XML_RSC_ATTR_CONTAINER);
+ CRM_META "_" PCMK__META_CONTAINER);
if (container != NULL) {
crm_trace("Resource %s is a part of container resource %s",
op->rsc_id, container);
- crm_xml_add(xml, XML_RSC_ATTR_CONTAINER, container);
+ crm_xml_add(xml, PCMK__META_CONTAINER, container);
}
}
@@ -946,7 +944,7 @@ controld_update_resource_history(const char *node_name,
* fenced for running a resource it isn't.
*/
crm_log_xml_trace(update, __func__);
- controld_update_cib(XML_CIB_TAG_STATUS, update, call_opt, cib_rsc_callback);
+ controld_update_cib(PCMK_XE_STATUS, update, call_opt, cib_rsc_callback);
free_xml(update);
}
@@ -963,15 +961,15 @@ controld_delete_action_history(const lrmd_event_data_t *op)
CRM_CHECK(op != NULL, return);
- xml_top = create_xml_node(NULL, XML_LRM_TAG_RSC_OP);
- crm_xml_add_int(xml_top, XML_LRM_ATTR_CALLID, op->call_id);
- crm_xml_add(xml_top, XML_ATTR_TRANSITION_KEY, op->user_data);
+ xml_top = pcmk__xe_create(NULL, PCMK__XE_LRM_RSC_OP);
+ crm_xml_add_int(xml_top, PCMK__XA_CALL_ID, op->call_id);
+ crm_xml_add(xml_top, PCMK__XA_TRANSITION_KEY, op->user_data);
if (op->interval_ms > 0) {
char *op_id = pcmk__op_key(op->rsc_id, op->op_type, op->interval_ms);
/* Avoid deleting last_failure too (if it was a result of this recurring op failing) */
- crm_xml_add(xml_top, XML_ATTR_ID, op_id);
+ crm_xml_add(xml_top, PCMK_XA_ID, op_id);
free(op_id);
}
@@ -979,31 +977,29 @@ controld_delete_action_history(const lrmd_event_data_t *op)
op->rsc_id, op->op_type, op->interval_ms, op->call_id);
controld_globals.cib_conn->cmds->remove(controld_globals.cib_conn,
- XML_CIB_TAG_STATUS, xml_top,
- cib_none);
+ PCMK_XE_STATUS, xml_top, cib_none);
crm_log_xml_trace(xml_top, "op:cancel");
free_xml(xml_top);
}
/* Define xpath to find LRM resource history entry by node and resource */
#define XPATH_HISTORY \
- "/" XML_TAG_CIB "/" 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']" \
- "/" XML_LRM_TAG_RSC_OP
+ "/" PCMK_XE_CIB "/" 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']" \
+ "/" PCMK__XE_LRM_RSC_OP
/* ... and also by operation key */
-#define XPATH_HISTORY_ID XPATH_HISTORY \
- "[@" XML_ATTR_ID "='%s']"
+#define XPATH_HISTORY_ID XPATH_HISTORY "[@" PCMK_XA_ID "='%s']"
/* ... and also by operation key and operation call ID */
#define XPATH_HISTORY_CALL XPATH_HISTORY \
- "[@" XML_ATTR_ID "='%s' and @" XML_LRM_ATTR_CALLID "='%d']"
+ "[@" PCMK_XA_ID "='%s' and @" PCMK__XA_CALL_ID "='%d']"
/* ... and also by operation key and original operation key */
#define XPATH_HISTORY_ORIG XPATH_HISTORY \
- "[@" XML_ATTR_ID "='%s' and @" XML_LRM_ATTR_TASK_KEY "='%s']"
+ "[@" PCMK_XA_ID "='%s' and @" PCMK__XA_OPERATION_KEY "='%s']"
/*!
* \internal
diff --git a/daemons/controld/controld_cib.h b/daemons/controld/controld_cib.h
index dcc5a48..9a8d6ac 100644
--- a/daemons/controld/controld_cib.h
+++ b/daemons/controld/controld_cib.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -16,7 +16,7 @@
#include <crm/crm.h>
#include <crm/common/xml.h>
-#include <crm/cib/internal.h> // PCMK__CIB_REQUEST_MODIFY
+#include <crm/cib/internal.h> // cib__*
#include "controld_globals.h" // controld_globals.cib_conn
static inline void
@@ -48,7 +48,7 @@ int controld_update_cib(const char *section, xmlNode *data, int options,
void *));
unsigned int cib_op_timeout(void);
-// Subsections of node_state
+// Subsections of PCMK__XE_NODE_STATE
enum controld_section_e {
controld_section_lrm,
controld_section_lrm_unlocked,
diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c
index 644d686..368659b 100644
--- a/daemons/controld/controld_control.c
+++ b/daemons/controld/controld_control.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 <sys/stat.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/pengine/rules.h>
#include <crm/cluster/internal.h>
#include <crm/cluster/election_internal.h>
@@ -27,10 +27,10 @@ static qb_ipcs_service_t *ipcs = NULL;
static crm_trigger_t *config_read_trigger = NULL;
#if SUPPORT_COROSYNC
-extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
+extern gboolean crm_connect_corosync(pcmk_cluster_t *cluster);
#endif
-void crm_shutdown(int nsig);
+static void crm_shutdown(int nsig);
static gboolean crm_read_options(gpointer user_data);
/* A_HA_CONNECT */
@@ -41,25 +41,25 @@ do_ha_control(long long action,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
gboolean registered = FALSE;
- static crm_cluster_t *cluster = NULL;
+ static pcmk_cluster_t *cluster = NULL;
if (cluster == NULL) {
cluster = pcmk_cluster_new();
}
if (action & A_HA_DISCONNECT) {
- crm_cluster_disconnect(cluster);
+ pcmk_cluster_disconnect(cluster);
crm_info("Disconnected from the cluster");
controld_set_fsa_input_flags(R_HA_DISCONNECTED);
}
if (action & A_HA_CONNECT) {
- crm_set_status_callback(&peer_update_callback);
- crm_set_autoreap(FALSE);
+ pcmk__cluster_set_status_callback(&peer_update_callback);
+ pcmk__cluster_set_autoreap(false);
#if SUPPORT_COROSYNC
- if (is_corosync_cluster()) {
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
registered = crm_connect_corosync(cluster);
}
#endif // SUPPORT_COROSYNC
@@ -117,7 +117,7 @@ do_shutdown_req(long long action,
pcmk__s(controld_globals.dc_name, "not set"));
msg = create_request(CRM_OP_SHUTDOWN_REQ, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
- if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
+ if (!pcmk__cluster_send_message(NULL, crm_msg_crmd, msg)) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
free_xml(msg);
@@ -241,7 +241,7 @@ crmd_exit(crm_exit_t exit_code)
controld_destroy_transition_trigger();
pcmk__client_cleanup();
- crm_peer_destroy();
+ pcmk__cluster_destroy_node_caches();
controld_free_fsa_timers();
te_cleanup_stonith_history_sync(NULL, TRUE);
@@ -365,7 +365,7 @@ accept_controller_client(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
{
crm_trace("Accepting new IPC client connection");
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -381,15 +381,17 @@ dispatch_controller_ipc(qb_ipcs_connection_t * c, void *data, size_t size)
xmlNode *msg = pcmk__client_data2xml(client, data, &id, &flags);
if (msg == NULL) {
- pcmk__ipc_send_ack(client, id, flags, "ack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL,
+ CRM_EX_PROTOCOL);
return 0;
}
- pcmk__ipc_send_ack(client, id, flags, "ack", NULL, CRM_EX_INDETERMINATE);
+ pcmk__ipc_send_ack(client, id, flags, PCMK__XE_ACK, NULL,
+ CRM_EX_INDETERMINATE);
CRM_ASSERT(client->user != NULL);
- pcmk__update_acl_user(msg, F_CRM_USER, client->user);
+ pcmk__update_acl_user(msg, PCMK__XA_CRM_USER, client->user);
- crm_xml_add(msg, F_CRM_SYS_FROM, client->id);
+ crm_xml_add(msg, PCMK__XA_CRM_SYS_FROM, client->id);
if (controld_authorize_ipc_message(msg, client, NULL)) {
crm_trace("Processing IPC message from client %s",
pcmk__client_name(client));
@@ -515,194 +517,6 @@ do_recover(long long action,
register_fsa_input(C_FSA_INTERNAL, I_TERMINATE, NULL);
}
-static pcmk__cluster_option_t controller_options[] = {
- /* name, old name, type, allowed values,
- * default value, validator,
- * short description,
- * long description
- */
- {
- "dc-version", NULL, "string", NULL, PCMK__VALUE_NONE, NULL,
- N_("Pacemaker version on cluster node elected Designated Controller (DC)"),
- N_("Includes a hash which identifies the exact changeset the code was "
- "built from. Used for diagnostic purposes.")
- },
- {
- "cluster-infrastructure", NULL, "string", NULL, "corosync", NULL,
- N_("The messaging stack on which Pacemaker is currently running"),
- N_("Used for informational and diagnostic purposes.")
- },
- {
- "cluster-name", NULL, "string", NULL, NULL, NULL,
- 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.")
- },
- {
- XML_CONFIG_ATTR_DC_DEADTIME, NULL, "time",
- NULL, "20s", pcmk__valid_interval_spec,
- 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.")
- },
- {
- XML_CONFIG_ATTR_RECHECK, NULL, "time",
- N_("Zero disables polling, while positive values are an interval in seconds"
- "(unless other units are specified, for example \"5min\")"),
- "15min", pcmk__valid_interval_spec,
- 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 timeouts 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.")
- },
- {
- "load-threshold", NULL, "percentage", NULL,
- "80%", pcmk__valid_percentage,
- 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"),
- },
- {
- "node-action-limit", NULL, "integer", NULL,
- "0", pcmk__valid_number,
- N_("Maximum number of jobs that can be scheduled per node "
- "(defaults to 2x cores)")
- },
- { XML_CONFIG_ATTR_FENCE_REACTION, NULL, "string", NULL, "stop", NULL,
- N_("How a cluster node should react if notified of its own fencing"),
- N_("A cluster node may receive notification of its own fencing if fencing "
- "is misconfigured, or if fabric fencing is in use that doesn't cut "
- "cluster communication. Allowed values are \"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.")
- },
- {
- XML_CONFIG_ATTR_ELECTION_FAIL, NULL, "time", NULL,
- "2min", pcmk__valid_interval_spec,
- "*** Advanced Use Only ***",
- 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.")
- },
- {
- XML_CONFIG_ATTR_FORCE_QUIT, NULL, "time", NULL,
- "20min", pcmk__valid_interval_spec,
- "*** Advanced Use Only ***",
- 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.")
- },
- {
- "join-integration-timeout", "crmd-integration-timeout", "time", NULL,
- "3min", pcmk__valid_interval_spec,
- "*** Advanced Use Only ***",
- N_("If you need to adjust this value, it probably indicates "
- "the presence of a bug.")
- },
- {
- "join-finalization-timeout", "crmd-finalization-timeout", "time", NULL,
- "30min", pcmk__valid_interval_spec,
- "*** Advanced Use Only ***",
- N_("If you need to adjust this value, it probably indicates "
- "the presence of a bug.")
- },
- {
- "transition-delay", "crmd-transition-delay", "time", NULL,
- "0s", pcmk__valid_interval_spec,
- N_("*** Advanced Use Only *** 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.")
- },
- {
- "stonith-watchdog-timeout", NULL, "time", NULL,
- "0", controld_verify_stonith_watchdog_timeout,
- 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 "
- "self-fence 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.")
- },
- {
- "stonith-max-attempts", NULL, "integer", NULL,
- "10", pcmk__valid_positive_number,
- N_("How many times fencing can fail before it will no longer be "
- "immediately re-attempted on a target")
- },
-
- // Already documented in libpe_status (other values must be kept identical)
- {
- "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
- },
- {
- 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.")
- },
- {
- 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.")
- },
-};
-
-void
-crmd_metadata(void)
-{
- const char *desc_short = "Pacemaker controller options";
- const char *desc_long = "Cluster options used by Pacemaker's controller";
-
- gchar *s = pcmk__format_option_metadata("pacemaker-controld", desc_short,
- desc_long, controller_options,
- PCMK__NELEM(controller_options));
- printf("%s", s);
- g_free(s);
-}
-
static void
config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
@@ -726,49 +540,62 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
}
crmconfig = output;
- if ((crmconfig != NULL)
- && !pcmk__xe_is(crmconfig, XML_CIB_TAG_CRMCONFIG)) {
- crmconfig = first_named_child(crmconfig, XML_CIB_TAG_CRMCONFIG);
+ if ((crmconfig != NULL) && !pcmk__xe_is(crmconfig, PCMK_XE_CRM_CONFIG)) {
+ crmconfig = pcmk__xe_first_child(crmconfig, PCMK_XE_CRM_CONFIG, NULL,
+ NULL);
}
if (!crmconfig) {
fsa_data_t *msg_data = NULL;
- crm_err("Local CIB query for " XML_CIB_TAG_CRMCONFIG " section failed");
+ crm_err("Local CIB query for " PCMK_XE_CRM_CONFIG " section failed");
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
goto bail;
}
crm_debug("Call %d : Parsing CIB options", call_id);
config_hash = pcmk__strkey_table(free, free);
- pe_unpack_nvpairs(crmconfig, crmconfig, XML_CIB_TAG_PROPSET, NULL,
- config_hash, CIB_OPTIONS_FIRST, FALSE, now, NULL);
+ pe_unpack_nvpairs(crmconfig, crmconfig, PCMK_XE_CLUSTER_PROPERTY_SET, NULL,
+ config_hash, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, FALSE, now,
+ NULL);
// Validate all options, and use defaults if not already present in hash
- pcmk__validate_cluster_options(config_hash, controller_options,
- PCMK__NELEM(controller_options));
+ pcmk__validate_cluster_options(config_hash);
- value = g_hash_table_lookup(config_hash, "no-quorum-policy");
- if (pcmk__str_eq(value, "suicide", pcmk__str_casei) && pcmk__locate_sbd()) {
+ /* Validate the watchdog timeout in the context of the local node
+ * environment. If invalid, the controller will exit with a fatal error.
+ *
+ * We do this via a wrapper in the controller, so that we call
+ * pcmk__valid_stonith_watchdog_timeout() only if watchdog fencing is
+ * enabled for the local node. Otherwise, we may exit unnecessarily.
+ *
+ * A validator function in libcrmcommon can't act as such a wrapper, because
+ * it doesn't have a stonith API connection or the local node name.
+ */
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_STONITH_WATCHDOG_TIMEOUT);
+ controld_verify_stonith_watchdog_timeout(value);
+
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_NO_QUORUM_POLICY);
+ if (pcmk__str_eq(value, PCMK_VALUE_FENCE_LEGACY, pcmk__str_casei)
+ && (pcmk__locate_sbd() != 0)) {
controld_set_global_flags(controld_no_quorum_suicide);
}
- value = g_hash_table_lookup(config_hash, XML_CONFIG_ATTR_SHUTDOWN_LOCK);
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK);
if (crm_is_true(value)) {
controld_set_global_flags(controld_shutdown_lock_enabled);
} else {
controld_clear_global_flags(controld_shutdown_lock_enabled);
}
- value = g_hash_table_lookup(config_hash,
- XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT);
- controld_globals.shutdown_lock_limit = crm_parse_interval_spec(value)
- / 1000;
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK_LIMIT);
+ pcmk_parse_interval_spec(value, &controld_globals.shutdown_lock_limit);
+ controld_globals.shutdown_lock_limit /= 1000;
- value = g_hash_table_lookup(config_hash,
- XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT);
- controld_globals.node_pending_timeout = crm_parse_interval_spec(value) / 1000;
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_NODE_PENDING_TIMEOUT);
+ pcmk_parse_interval_spec(value, &controld_globals.node_pending_timeout);
+ controld_globals.node_pending_timeout /= 1000;
- value = g_hash_table_lookup(config_hash, "cluster-name");
+ value = g_hash_table_lookup(config_hash, PCMK_OPT_CLUSTER_NAME);
pcmk__str_update(&(controld_globals.cluster_name), value);
// Let subcomponents initialize their own static variables
@@ -777,7 +604,7 @@ config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
controld_configure_fsa_timers(config_hash);
controld_configure_throttle(config_hash);
- alerts = first_named_child(output, XML_CIB_TAG_ALERTS);
+ alerts = pcmk__xe_first_child(output, PCMK_XE_ALERTS, NULL, NULL);
crmd_unpack_alerts(alerts);
controld_set_fsa_input_flags(R_READ_CONFIG);
@@ -809,8 +636,8 @@ crm_read_options(gpointer user_data)
{
cib_t *cib_conn = controld_globals.cib_conn;
int call_id = cib_conn->cmds->query(cib_conn,
- "//" XML_CIB_TAG_CRMCONFIG
- " | //" XML_CIB_TAG_ALERTS,
+ "//" PCMK_XE_CRM_CONFIG
+ " | //" PCMK_XE_ALERTS,
NULL, cib_xpath|cib_scope_local);
fsa_register_cib_callback(call_id, NULL, config_query_callback);
@@ -829,7 +656,7 @@ do_read_config(long long action,
controld_trigger_config();
}
-void
+static void
crm_shutdown(int nsig)
{
const char *value = NULL;
@@ -856,9 +683,7 @@ crm_shutdown(int nsig)
* config_query_callback() has been run at least once, it doesn't look like
* anything could have changed the timer period since then.
*/
- value = pcmk__cluster_option(NULL, controller_options,
- PCMK__NELEM(controller_options),
- XML_CONFIG_ATTR_FORCE_QUIT);
- default_period_ms = crm_parse_interval_spec(value);
+ value = pcmk__cluster_option(NULL, PCMK_OPT_SHUTDOWN_ESCALATION);
+ pcmk_parse_interval_spec(value, &default_period_ms);
controld_shutdown_start_countdown(default_period_ms);
}
diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
index b69e821..d0652e4 100644
--- a/daemons/controld/controld_corosync.c
+++ b/daemons/controld/controld_corosync.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,14 +31,14 @@ crmd_cs_dispatch(cpg_handle_t handle, const struct cpg_name *groupName,
{
uint32_t kind = 0;
const char *from = NULL;
- char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
+ char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
if(data == NULL) {
return;
}
if (kind == crm_class_cluster) {
crm_node_t *peer = NULL;
- xmlNode *xml = string2xml(data);
+ xmlNode *xml = pcmk__xml_parse(data);
if (xml == NULL) {
crm_err("Could not parse message content (%d): %.100s", kind, data);
@@ -46,10 +46,9 @@ crmd_cs_dispatch(cpg_handle_t handle, const struct cpg_name *groupName,
return;
}
- crm_xml_add(xml, F_ORIG, from);
- /* crm_xml_add_int(xml, F_SEQ, wrapper->id); Fake? */
+ crm_xml_add(xml, PCMK__XA_SRC, from);
- peer = crm_get_peer(0, from);
+ peer = pcmk__get_node(0, from, NULL, pcmk__node_search_cluster_member);
if (!pcmk_is_set(peer->processes, crm_proc_cpg)) {
/* If we can still talk to our peer process on that node,
* then it must be part of the corosync membership
@@ -57,7 +56,7 @@ crmd_cs_dispatch(cpg_handle_t handle, const struct cpg_name *groupName,
crm_warn("Receiving messages from a node we think is dead: %s[%d]",
peer->uname, peer->id);
crm_update_peer_proc(__func__, peer, crm_proc_cpg,
- ONLINESTATUS);
+ PCMK_VALUE_ONLINE);
}
crmd_ha_msg_filter(xml);
free_xml(xml);
@@ -119,8 +118,8 @@ cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
if (controld_globals.dc_name != NULL) {
crm_node_t *peer = NULL;
- peer = pcmk__search_cluster_node_cache(0, controld_globals.dc_name,
- NULL);
+ peer = pcmk__search_node_caches(0, controld_globals.dc_name,
+ pcmk__node_search_cluster_member);
if (peer != NULL) {
for (int i = 0; i < left_list_entries; ++i) {
if (left_list[i].nodeid == peer->id) {
@@ -132,25 +131,26 @@ cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
}
// Process the change normally, which will call the peer callback as needed
- pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries,
- left_list, left_list_entries,
- joined_list, joined_list_entries);
+ pcmk__cpg_confchg_cb(handle, cpg_name, member_list, member_list_entries,
+ left_list, left_list_entries,
+ joined_list, joined_list_entries);
controld_clear_global_flags(controld_dc_left);
}
-extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
+extern gboolean crm_connect_corosync(pcmk_cluster_t *cluster);
gboolean
-crm_connect_corosync(crm_cluster_t * cluster)
+crm_connect_corosync(pcmk_cluster_t *cluster)
{
- if (is_corosync_cluster()) {
- crm_set_status_callback(&peer_update_callback);
- cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
- cluster->cpg.cpg_confchg_fn = cpg_membership_callback;
- cluster->destroy = crmd_cs_destroy;
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
+ pcmk__cluster_set_status_callback(&peer_update_callback);
- if (crm_cluster_connect(cluster)) {
+ pcmk_cluster_set_destroy_fn(cluster, crmd_cs_destroy);
+ pcmk_cpg_set_deliver_fn(cluster, crmd_cs_dispatch);
+ pcmk_cpg_set_confchg_fn(cluster, cpg_membership_callback);
+
+ if (pcmk_cluster_connect(cluster) == pcmk_rc_ok) {
pcmk__corosync_quorum_connect(crmd_quorum_callback,
crmd_cs_destroy);
return TRUE;
diff --git a/daemons/controld/controld_election.c b/daemons/controld/controld_election.c
index 70ffecc..1d4ff25 100644
--- a/daemons/controld/controld_election.c
+++ b/daemons/controld/controld_election.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/time.h>
#include <sys/resource.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
#include <crm/cluster/election_internal.h>
@@ -44,10 +43,11 @@ controld_election_init(const char *uname)
void
controld_configure_election(GHashTable *options)
{
- const char *value = NULL;
+ const char *value = g_hash_table_lookup(options, PCMK_OPT_ELECTION_TIMEOUT);
+ guint interval_ms = 0U;
- value = g_hash_table_lookup(options, XML_CONFIG_ATTR_ELECTION_FAIL);
- election_timeout_set_period(fsa_election, crm_parse_interval_spec(value));
+ pcmk_parse_interval_spec(value, &interval_ms);
+ election_timeout_set_period(fsa_election, interval_ms);
}
void
@@ -201,7 +201,7 @@ feature_update_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, vo
#define dc_takeover_update_attr(name, value) do { \
cib__update_node_attr(controld_globals.logger_out, \
controld_globals.cib_conn, cib_none, \
- XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL, \
+ PCMK_XE_CRM_CONFIG, NULL, NULL, NULL, NULL, \
name, value, NULL, NULL); \
} while (0)
@@ -213,7 +213,8 @@ do_dc_takeover(long long action,
enum crmd_fsa_input current_input, fsa_data_t * msg_data)
{
xmlNode *cib = NULL;
- const char *cluster_type = name_for_cluster_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);
pid_t watchdog = pcmk__locate_sbd();
crm_info("Taking over DC status for this partition");
@@ -226,20 +227,23 @@ do_dc_takeover(long long action,
controld_globals.cib_conn->cmds->set_primary(controld_globals.cib_conn,
cib_scope_local);
- cib = create_xml_node(NULL, XML_TAG_CIB);
- crm_xml_add(cib, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
- controld_update_cib(XML_TAG_CIB, cib, cib_none, feature_update_callback);
+ cib = pcmk__xe_create(NULL, PCMK_XE_CIB);
+ crm_xml_add(cib, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
+ controld_update_cib(PCMK_XE_CIB, cib, cib_none, feature_update_callback);
- dc_takeover_update_attr(XML_ATTR_HAVE_WATCHDOG, pcmk__btoa(watchdog));
- dc_takeover_update_attr("dc-version", PACEMAKER_VERSION "-" BUILD_VERSION);
- dc_takeover_update_attr("cluster-infrastructure", cluster_type);
+ dc_takeover_update_attr(PCMK_OPT_HAVE_WATCHDOG, pcmk__btoa(watchdog));
+ dc_takeover_update_attr(PCMK_OPT_DC_VERSION,
+ PACEMAKER_VERSION "-" BUILD_VERSION);
+ dc_takeover_update_attr(PCMK_OPT_CLUSTER_INFRASTRUCTURE, cluster_layer_s);
#if SUPPORT_COROSYNC
- if ((controld_globals.cluster_name == NULL) && is_corosync_cluster()) {
+ if ((controld_globals.cluster_name == NULL)
+ && (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync)) {
+
char *cluster_name = pcmk__corosync_cluster_name();
if (cluster_name != NULL) {
- dc_takeover_update_attr("cluster-name", cluster_name);
+ dc_takeover_update_attr(PCMK_OPT_CLUSTER_NAME, cluster_name);
}
free(cluster_name);
}
@@ -265,13 +269,15 @@ do_dc_release(long long action,
crm_info("DC role released");
if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
xmlNode *update = NULL;
- crm_node_t *node = crm_get_peer(0, controld_globals.our_nodename);
+ crm_node_t *node =
+ pcmk__get_node(0, controld_globals.our_nodename,
+ NULL, pcmk__node_search_cluster_member);
pcmk__update_peer_expected(__func__, node, CRMD_JOINSTATE_DOWN);
update = create_node_state_update(node, node_update_expected, NULL,
__func__);
/* Don't need a based response because controld will stop. */
- fsa_cib_anon_update_discard_reply(XML_CIB_TAG_STATUS, update);
+ fsa_cib_anon_update_discard_reply(PCMK_XE_STATUS, update);
free_xml(update);
}
register_fsa_input(C_FSA_INTERNAL, I_RELEASE_SUCCESS, NULL);
@@ -280,6 +286,5 @@ do_dc_release(long long action,
crm_err("Unknown DC action %s", fsa_action2string(action));
}
- crm_trace("Am I still the DC? %s", AM_I_DC ? XML_BOOLEAN_YES : XML_BOOLEAN_NO);
-
+ crm_trace("Am I still the DC? %s", pcmk__btoa(AM_I_DC));
}
diff --git a/daemons/controld/controld_execd.c b/daemons/controld/controld_execd.c
index 480d37d..917c8c0 100644
--- a/daemons/controld/controld_execd.c
+++ b/daemons/controld/controld_execd.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/lrmd.h> // lrmd_event_data_t, lrmd_rsc_info_t, etc.
#include <crm/services.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/pengine/rules.h>
#include <crm/lrmd_internal.h>
@@ -68,7 +67,7 @@ static void
copy_instance_keys(gpointer key, gpointer value, gpointer user_data)
{
if (strstr(key, CRM_META "_") == NULL) {
- g_hash_table_replace(user_data, strdup((const char *)key), strdup((const char *)value));
+ pcmk__insert_dup(user_data, (const char *) key, (const char *) value);
}
}
@@ -76,7 +75,7 @@ static void
copy_meta_keys(gpointer key, gpointer value, gpointer user_data)
{
if (strstr(key, CRM_META "_") != NULL) {
- g_hash_table_replace(user_data, strdup((const char *)key), strdup((const char *)value));
+ pcmk__insert_dup(user_data, (const char *) key, (const char *) value);
}
}
@@ -175,14 +174,14 @@ update_history_cache(lrm_state_t * lrm_state, lrmd_rsc_info_t * rsc, lrmd_event_
entry = g_hash_table_lookup(lrm_state->resource_history, op->rsc_id);
if (entry == NULL && rsc) {
- entry = calloc(1, sizeof(rsc_history_t));
- entry->id = strdup(op->rsc_id);
+ entry = pcmk__assert_alloc(1, sizeof(rsc_history_t));
+ entry->id = pcmk__str_copy(op->rsc_id);
g_hash_table_insert(lrm_state->resource_history, entry->id, entry);
entry->rsc.id = entry->id;
- entry->rsc.type = strdup(rsc->type);
- entry->rsc.standard = strdup(rsc->standard);
- pcmk__str_update(&entry->rsc.provider, rsc->provider);
+ entry->rsc.type = pcmk__str_copy(rsc->type);
+ entry->rsc.standard = pcmk__str_copy(rsc->standard);
+ entry->rsc.provider = pcmk__str_copy(rsc->provider);
} else if (entry == NULL) {
crm_info("Resource %s no longer exists, not updating cache", op->rsc_id);
@@ -539,18 +538,21 @@ build_active_RAs(lrm_state_t * lrm_state, xmlNode * rsc_list)
while (g_hash_table_iter_next(&iter, NULL, (void **)&entry)) {
GList *gIter = NULL;
- xmlNode *xml_rsc = create_xml_node(rsc_list, XML_LRM_TAG_RESOURCE);
+ xmlNode *xml_rsc = pcmk__xe_create(rsc_list, PCMK__XE_LRM_RESOURCE);
- crm_xml_add(xml_rsc, XML_ATTR_ID, entry->id);
- crm_xml_add(xml_rsc, XML_ATTR_TYPE, entry->rsc.type);
- crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, entry->rsc.standard);
- crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, entry->rsc.provider);
+ crm_xml_add(xml_rsc, PCMK_XA_ID, entry->id);
+ crm_xml_add(xml_rsc, PCMK_XA_TYPE, entry->rsc.type);
+ crm_xml_add(xml_rsc, PCMK_XA_CLASS, entry->rsc.standard);
+ crm_xml_add(xml_rsc, PCMK_XA_PROVIDER, entry->rsc.provider);
if (entry->last && entry->last->params) {
- const char *container = g_hash_table_lookup(entry->last->params, CRM_META"_"XML_RSC_ATTR_CONTAINER);
+ static const char *name = CRM_META "_" PCMK__META_CONTAINER;
+ const char *container = g_hash_table_lookup(entry->last->params,
+ name);
+
if (container) {
crm_trace("Resource %s is a part of container resource %s", entry->id, container);
- crm_xml_add(xml_rsc, XML_RSC_ATTR_CONTAINER, container);
+ crm_xml_add(xml_rsc, PCMK__META_CONTAINER, container);
}
}
controld_add_resource_history_xml(xml_rsc, &(entry->rsc), entry->failed,
@@ -581,7 +583,7 @@ controld_query_executor_state(void)
return NULL;
}
- peer = crm_get_peer_full(0, lrm_state->node_name, CRM_GET_PEER_ANY);
+ peer = pcmk__get_node(0, lrm_state->node_name, NULL, pcmk__node_search_any);
CRM_CHECK(peer != NULL, return NULL);
xml_state = create_node_state_update(peer,
@@ -591,9 +593,9 @@ controld_query_executor_state(void)
return NULL;
}
- xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM);
- crm_xml_add(xml_data, XML_ATTR_ID, peer->uuid);
- rsc_list = create_xml_node(xml_data, XML_LRM_TAG_RESOURCES);
+ xml_data = pcmk__xe_create(xml_state, PCMK__XE_LRM);
+ crm_xml_add(xml_data, PCMK_XA_ID, peer->uuid);
+ rsc_list = pcmk__xe_create(xml_data, PCMK__XE_LRM_RESOURCES);
/* Build a list of active (not always running) resources */
build_active_RAs(lrm_state, rsc_list);
@@ -651,7 +653,7 @@ controld_trigger_delete_refresh(const char *from_sys, const char *rsc_id)
crm_debug("Triggering a refresh after %s cleaned %s", from_sys, rsc_id);
cib__update_node_attr(controld_globals.logger_out,
controld_globals.cib_conn, cib_none,
- XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL,
+ PCMK_XE_CRM_CONFIG, NULL, NULL, NULL, NULL,
"last-lrm-refresh", now_s, NULL, NULL);
free(now_s);
}
@@ -661,8 +663,8 @@ static void
notify_deleted(lrm_state_t * lrm_state, ha_msg_input_t * input, const char *rsc_id, int rc)
{
lrmd_event_data_t *op = NULL;
- const char *from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM);
- const char *from_host = crm_element_value(input->msg, F_CRM_HOST_FROM);
+ const char *from_sys = crm_element_value(input->msg, PCMK__XA_CRM_SYS_FROM);
+ const char *from_host = crm_element_value(input->msg, PCMK__XA_SRC);
crm_info("Notifying %s on %s that %s was%s deleted",
from_sys, (from_host? from_host : "localhost"), rsc_id,
@@ -711,7 +713,7 @@ delete_rsc_entry(lrm_state_t *lrm_state, ha_msg_input_t *input,
CRM_CHECK(rsc_id != NULL, return);
if (rc == pcmk_ok) {
- char *rsc_id_copy = strdup(rsc_id);
+ char *rsc_id_copy = pcmk__str_copy(rsc_id);
if (rsc_iter) {
g_hash_table_iter_remove(rsc_iter);
@@ -907,7 +909,7 @@ static int
get_lrm_resource(lrm_state_t *lrm_state, const xmlNode *rsc_xml,
gboolean do_create, lrmd_rsc_info_t **rsc_info)
{
- const char *id = ID(rsc_xml);
+ const char *id = pcmk__xe_id(rsc_xml);
CRM_CHECK(lrm_state && rsc_xml && rsc_info, return -EINVAL);
CRM_CHECK(id, return -EINVAL);
@@ -921,7 +923,7 @@ get_lrm_resource(lrm_state_t *lrm_state, const xmlNode *rsc_xml,
// If resource isn't known by ID, try clone name, if provided
if (!*rsc_info) {
- const char *long_id = crm_element_value(rsc_xml, XML_ATTR_ID_LONG);
+ const char *long_id = crm_element_value(rsc_xml, PCMK__XA_LONG_ID);
if (long_id) {
*rsc_info = lrm_state_get_rsc_info(lrm_state, long_id, 0);
@@ -929,9 +931,9 @@ get_lrm_resource(lrm_state_t *lrm_state, const xmlNode *rsc_xml,
}
if ((*rsc_info == NULL) && do_create) {
- const char *class = crm_element_value(rsc_xml, XML_AGENT_ATTR_CLASS);
- const char *provider = crm_element_value(rsc_xml, XML_AGENT_ATTR_PROVIDER);
- const char *type = crm_element_value(rsc_xml, XML_ATTR_TYPE);
+ const char *class = crm_element_value(rsc_xml, PCMK_XA_CLASS);
+ const char *provider = crm_element_value(rsc_xml, PCMK_XA_PROVIDER);
+ const char *type = crm_element_value(rsc_xml, PCMK_XA_TYPE);
int rc;
crm_trace("Registering resource %s with the executor", id);
@@ -979,10 +981,10 @@ delete_resource(lrm_state_t *lrm_state, const char *id, lrmd_rsc_info_t *rsc,
crm_info("Deletion of resource '%s' from executor is pending", id);
if (request) {
struct pending_deletion_op_s *op = NULL;
- char *ref = crm_element_value_copy(request->msg, XML_ATTR_REFERENCE);
+ char *ref = crm_element_value_copy(request->msg, PCMK_XA_REFERENCE);
- op = calloc(1, sizeof(struct pending_deletion_op_s));
- op->rsc = strdup(rsc->id);
+ op = pcmk__assert_alloc(1, sizeof(struct pending_deletion_op_s));
+ op->rsc = pcmk__str_copy(rsc->id);
op->input = copy_ha_msg_input(request);
g_hash_table_insert(lrm_state->deletion_ops, ref, op);
}
@@ -1096,25 +1098,26 @@ synthesize_lrmd_failure(lrm_state_t *lrm_state, const xmlNode *action,
const char *exit_reason)
{
lrmd_event_data_t *op = NULL;
- const char *operation = crm_element_value(action, XML_LRM_ATTR_TASK);
- const char *target_node = crm_element_value(action, XML_LRM_ATTR_TARGET);
- xmlNode *xml_rsc = find_xml_node(action, XML_CIB_TAG_RESOURCE, TRUE);
+ const char *operation = crm_element_value(action, PCMK_XA_OPERATION);
+ const char *target_node = crm_element_value(action, PCMK__META_ON_NODE);
+ xmlNode *xml_rsc = pcmk__xe_first_child(action, PCMK_XE_PRIMITIVE, NULL,
+ NULL);
- if ((xml_rsc == NULL) || (ID(xml_rsc) == NULL)) {
+ if ((xml_rsc == NULL) || (pcmk__xe_id(xml_rsc) == NULL)) {
/* @TODO Should we do something else, like direct ack? */
crm_info("Can't fake %s failure (%d) on %s without resource configuration",
- crm_element_value(action, XML_LRM_ATTR_TASK_KEY), rc,
+ crm_element_value(action, PCMK__XA_OPERATION_KEY), rc,
target_node);
return;
} else if(operation == NULL) {
/* This probably came from crm_resource -C, nothing to do */
crm_info("Can't fake %s failure (%d) on %s without operation",
- ID(xml_rsc), rc, target_node);
+ pcmk__xe_id(xml_rsc), rc, target_node);
return;
}
- op = construct_op(lrm_state, action, ID(xml_rsc), operation);
+ op = construct_op(lrm_state, action, pcmk__xe_id(xml_rsc), operation);
if (pcmk__str_eq(operation, PCMK_ACTION_NOTIFY, pcmk__str_casei)) {
// Notifications can't fail
@@ -1146,7 +1149,7 @@ lrm_op_target(const xmlNode *xml)
const char *target = NULL;
if (xml) {
- target = crm_element_value(xml, XML_LRM_ATTR_TARGET);
+ target = crm_element_value(xml, PCMK__META_ON_NODE);
}
if (target == NULL) {
target = controld_globals.our_nodename;
@@ -1160,7 +1163,7 @@ fail_lrm_resource(xmlNode *xml, lrm_state_t *lrm_state, const char *user_name,
{
lrmd_event_data_t *op = NULL;
lrmd_rsc_info_t *rsc = NULL;
- xmlNode *xml_rsc = find_xml_node(xml, XML_CIB_TAG_RESOURCE, TRUE);
+ xmlNode *xml_rsc = pcmk__xe_first_child(xml, PCMK_XE_PRIMITIVE, NULL, NULL);
CRM_CHECK(xml_rsc != NULL, return);
@@ -1172,18 +1175,20 @@ fail_lrm_resource(xmlNode *xml, lrm_state_t *lrm_state, const char *user_name,
* and pass that event to the executor client callback so it will be
* processed as if it came from the executor.
*/
- op = construct_op(lrm_state, xml, ID(xml_rsc), "asyncmon");
+ op = construct_op(lrm_state, xml, pcmk__xe_id(xml_rsc), "asyncmon");
free((char*) op->user_data);
op->user_data = NULL;
op->interval_ms = 0;
if (user_name && !pcmk__is_privileged(user_name)) {
- crm_err("%s does not have permission to fail %s", user_name, ID(xml_rsc));
+ crm_err("%s does not have permission to fail %s",
+ user_name, pcmk__xe_id(xml_rsc));
fake_op_status(lrm_state, op, PCMK_EXEC_ERROR,
PCMK_OCF_INSUFFICIENT_PRIV,
"Unprivileged user cannot fail resources");
- controld_ack_event_directly(from_host, from_sys, NULL, op, ID(xml_rsc));
+ controld_ack_event_directly(from_host, from_sys, NULL, op,
+ pcmk__xe_id(xml_rsc));
lrmd_free_event(op);
return;
}
@@ -1204,7 +1209,8 @@ fail_lrm_resource(xmlNode *xml, lrm_state_t *lrm_state, const char *user_name,
"Cannot fail unknown resource");
}
- controld_ack_event_directly(from_host, from_sys, NULL, op, ID(xml_rsc));
+ controld_ack_event_directly(from_host, from_sys, NULL, op,
+ pcmk__xe_id(xml_rsc));
lrmd_free_event(op);
}
@@ -1242,16 +1248,17 @@ static bool do_lrm_cancel(ha_msg_input_t *input, lrm_state_t *lrm_state,
const char *op_task = NULL;
guint interval_ms = 0;
gboolean in_progress = FALSE;
- xmlNode *params = find_xml_node(input->xml, XML_TAG_ATTRS, TRUE);
+ xmlNode *params = pcmk__xe_first_child(input->xml, PCMK__XE_ATTRIBUTES,
+ NULL, NULL);
CRM_CHECK(params != NULL, return FALSE);
- meta_key = crm_meta_name(XML_LRM_ATTR_TASK);
+ meta_key = crm_meta_name(PCMK_XA_OPERATION);
op_task = crm_element_value(params, meta_key);
free(meta_key);
CRM_CHECK(op_task != NULL, return FALSE);
- meta_key = crm_meta_name(XML_LRM_ATTR_INTERVAL_MS);
+ meta_key = crm_meta_name(PCMK_META_INTERVAL);
if (crm_element_value_ms(params, meta_key, &interval_ms) != pcmk_ok) {
free(meta_key);
return FALSE;
@@ -1260,7 +1267,7 @@ static bool do_lrm_cancel(ha_msg_input_t *input, lrm_state_t *lrm_state,
op_key = pcmk__op_key(rsc->id, op_task, interval_ms);
- meta_key = crm_meta_name(XML_LRM_ATTR_CALLID);
+ meta_key = crm_meta_name(PCMK__XA_CALL_ID);
call_id = crm_element_value(params, meta_key);
free(meta_key);
@@ -1302,7 +1309,8 @@ static bool do_lrm_cancel(ha_msg_input_t *input, lrm_state_t *lrm_state,
* not abcdaa8, they will time out waiting for the ack (no
* released versions of Pacemaker are affected).
*/
- const char *peer_version = crm_element_value(params, XML_ATTR_CRM_VERSION);
+ const char *peer_version = crm_element_value(params,
+ PCMK_XA_CRM_FEATURE_SET);
if (compare_version(peer_version, "3.0.8") <= 0) {
crm_info("Sending compatibility ack for %s cancellation to %s (CRM version %s)",
@@ -1359,9 +1367,8 @@ new_metadata_cb_data(lrmd_rsc_info_t *rsc, xmlNode *input_xml)
{
struct metadata_cb_data *data = NULL;
- data = calloc(1, sizeof(struct metadata_cb_data));
- CRM_ASSERT(data != NULL);
- data->input_xml = copy_xml(input_xml);
+ data = pcmk__assert_alloc(1, sizeof(struct metadata_cb_data));
+ data->input_xml = pcmk__xml_copy(NULL, input_xml);
data->rsc = lrmd_copy_rsc_info(rsc);
return data;
}
@@ -1433,11 +1440,11 @@ do_lrm_invoke(long long action,
}
CRM_ASSERT(lrm_state != NULL);
- user_name = pcmk__update_acl_user(input->msg, F_CRM_USER, NULL);
- crm_op = crm_element_value(input->msg, F_CRM_TASK);
- from_sys = crm_element_value(input->msg, F_CRM_SYS_FROM);
+ user_name = pcmk__update_acl_user(input->msg, PCMK__XA_CRM_USER, NULL);
+ crm_op = crm_element_value(input->msg, PCMK__XA_CRM_TASK);
+ from_sys = crm_element_value(input->msg, PCMK__XA_CRM_SYS_FROM);
if (!pcmk__str_eq(from_sys, CRM_SYSTEM_TENGINE, pcmk__str_none)) {
- from_host = crm_element_value(input->msg, F_CRM_HOST_FROM);
+ from_host = crm_element_value(input->msg, PCMK__XA_SRC);
}
if (pcmk__str_eq(crm_op, PCMK_ACTION_LRM_DELETE, pcmk__str_none)) {
@@ -1447,7 +1454,7 @@ do_lrm_invoke(long long action,
operation = PCMK_ACTION_DELETE;
} else if (input->xml != NULL) {
- operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK);
+ operation = crm_element_value(input->xml, PCMK_XA_OPERATION);
}
CRM_CHECK(!pcmk__str_empty(crm_op) || !pcmk__str_empty(operation), return);
@@ -1471,7 +1478,7 @@ do_lrm_invoke(long long action,
// @COMPAT DCs <1.1.14 in a rolling upgrade might schedule this op
} else if (pcmk__str_eq(operation, CRM_OP_PROBED, pcmk__str_none)) {
- update_attrd(lrm_state->node_name, CRM_OP_PROBED, XML_BOOLEAN_TRUE,
+ update_attrd(lrm_state->node_name, CRM_OP_PROBED, PCMK_VALUE_TRUE,
user_name, is_remote_node);
} else if (pcmk__str_eq(crm_op, CRM_OP_REPROBE, pcmk__str_none)
@@ -1480,20 +1487,21 @@ do_lrm_invoke(long long action,
if (input->xml != NULL) {
// For CRM_OP_REPROBE, a NULL target means we're targeting all nodes
- raw_target = crm_element_value(input->xml, XML_LRM_ATTR_TARGET);
+ raw_target = crm_element_value(input->xml, PCMK__META_ON_NODE);
}
handle_reprobe_op(lrm_state, from_sys, from_host, user_name,
is_remote_node, (raw_target == NULL));
} else if (operation != NULL) {
lrmd_rsc_info_t *rsc = NULL;
- xmlNode *xml_rsc = find_xml_node(input->xml, XML_CIB_TAG_RESOURCE, TRUE);
+ xmlNode *xml_rsc = pcmk__xe_first_child(input->xml, PCMK_XE_PRIMITIVE,
+ NULL, NULL);
gboolean create_rsc = !pcmk__str_eq(operation, PCMK_ACTION_DELETE,
pcmk__str_none);
int rc;
// We can't return anything meaningful without a resource ID
- CRM_CHECK(xml_rsc && ID(xml_rsc), return);
+ CRM_CHECK((xml_rsc != NULL) && (pcmk__xe_id(xml_rsc) != NULL), return);
rc = get_lrm_resource(lrm_state, xml_rsc, create_rsc, &rsc);
if (rc == -ENOTCONN) {
@@ -1509,15 +1517,15 @@ do_lrm_invoke(long long action,
*/
crm_notice("Not registering resource '%s' for a %s event "
CRM_XS " get-rc=%d (%s) transition-key=%s",
- ID(xml_rsc), operation,
- rc, pcmk_strerror(rc), ID(input->xml));
- delete_rsc_entry(lrm_state, input, ID(xml_rsc), NULL, pcmk_ok,
- user_name, true);
+ pcmk__xe_id(xml_rsc), operation,
+ rc, pcmk_strerror(rc), pcmk__xe_id(input->xml));
+ delete_rsc_entry(lrm_state, input, pcmk__xe_id(xml_rsc), NULL,
+ pcmk_ok, user_name, true);
return;
} else if (rc == -EINVAL) {
// Resource operation on malformed resource
- crm_err("Invalid resource definition for %s", ID(xml_rsc));
+ crm_err("Invalid resource definition for %s", pcmk__xe_id(xml_rsc));
crm_log_xml_warn(input->msg, "invalid resource");
synthesize_lrmd_failure(lrm_state, input->xml, PCMK_EXEC_ERROR,
PCMK_OCF_NOT_CONFIGURED, // fatal error
@@ -1528,7 +1536,7 @@ do_lrm_invoke(long long action,
// Error communicating with the executor
crm_err("Could not register resource '%s' with executor: %s "
CRM_XS " rc=%d",
- ID(xml_rsc), pcmk_strerror(rc), rc);
+ pcmk__xe_id(xml_rsc), pcmk_strerror(rc), rc);
crm_log_xml_warn(input->msg, "failed registration");
synthesize_lrmd_failure(lrm_state, input->xml, PCMK_EXEC_ERROR,
PCMK_OCF_INVALID_PARAM, // hard error
@@ -1632,30 +1640,30 @@ construct_op(const lrm_state_t *lrm_state, const xmlNode *rsc_op,
*/
op->params = pcmk__strkey_table(free, free);
- g_hash_table_insert(op->params, strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
+ pcmk__insert_dup(op->params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
crm_trace("Constructed %s op for %s", operation, rsc_id);
return op;
}
params = xml2list(rsc_op);
- g_hash_table_remove(params, CRM_META "_op_target_rc");
+ g_hash_table_remove(params, CRM_META "_" PCMK__META_OP_TARGET_RC);
- op_delay = crm_meta_value(params, XML_OP_ATTR_START_DELAY);
+ op_delay = crm_meta_value(params, PCMK_META_START_DELAY);
pcmk__scan_min_int(op_delay, &op->start_delay, 0);
- op_timeout = crm_meta_value(params, XML_ATTR_TIMEOUT);
+ op_timeout = crm_meta_value(params, PCMK_META_TIMEOUT);
pcmk__scan_min_int(op_timeout, &op->timeout, 0);
- if (pcmk__guint_from_hash(params, CRM_META "_" XML_LRM_ATTR_INTERVAL_MS, 0,
+ if (pcmk__guint_from_hash(params, CRM_META "_" PCMK_META_INTERVAL, 0,
&(op->interval_ms)) != pcmk_rc_ok) {
op->interval_ms = 0;
}
/* Use pcmk_monitor_timeout instead of meta timeout for stonith
recurring monitor, if set */
- primitive = find_xml_node(rsc_op, XML_CIB_TAG_RESOURCE, FALSE);
- class = crm_element_value(primitive, XML_AGENT_ATTR_CLASS);
+ primitive = pcmk__xe_first_child(rsc_op, PCMK_XE_PRIMITIVE, NULL, NULL);
+ class = crm_element_value(primitive, PCMK_XA_CLASS);
if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_fence_params)
&& pcmk__str_eq(operation, PCMK_ACTION_MONITOR, pcmk__str_casei)
@@ -1663,7 +1671,9 @@ construct_op(const lrm_state_t *lrm_state, const xmlNode *rsc_op,
op_timeout = g_hash_table_lookup(params, "pcmk_monitor_timeout");
if (op_timeout != NULL) {
- op->timeout = crm_get_msec(op_timeout);
+ long long timeout_ms = crm_get_msec(op_timeout);
+
+ op->timeout = (int) QB_MIN(timeout_ms, INT_MAX);
}
}
@@ -1701,10 +1711,10 @@ construct_op(const lrm_state_t *lrm_state, const xmlNode *rsc_op,
op->start_delay = 0;
}
- transition = crm_element_value(rsc_op, XML_ATTR_TRANSITION_KEY);
+ transition = crm_element_value(rsc_op, PCMK__XA_TRANSITION_KEY);
CRM_CHECK(transition != NULL, return op);
- op->user_data = strdup(transition);
+ op->user_data = pcmk__str_copy(transition);
if (op->interval_ms != 0) {
if (pcmk__strcase_any_of(operation, PCMK_ACTION_START, PCMK_ACTION_STOP,
@@ -1745,23 +1755,25 @@ controld_ack_event_directly(const char *to_host, const char *to_sys,
CRM_CHECK(op != NULL, return);
if (op->rsc_id == NULL) {
+ // op->rsc_id is a (const char *) but lrmd_free_event() frees it
CRM_ASSERT(rsc_id != NULL);
- op->rsc_id = strdup(rsc_id);
+ op->rsc_id = pcmk__str_copy(rsc_id);
}
if (to_sys == NULL) {
to_sys = CRM_SYSTEM_TENGINE;
}
- peer = crm_get_peer(0, controld_globals.our_nodename);
+ peer = pcmk__get_node(0, controld_globals.our_nodename, NULL,
+ pcmk__node_search_cluster_member);
update = create_node_state_update(peer, node_update_none, NULL,
__func__);
- iter = create_xml_node(update, XML_CIB_TAG_LRM);
- crm_xml_add(iter, XML_ATTR_ID, controld_globals.our_uuid);
- iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES);
- iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE);
+ iter = pcmk__xe_create(update, PCMK__XE_LRM);
+ crm_xml_add(iter, PCMK_XA_ID, controld_globals.our_uuid);
+ iter = pcmk__xe_create(iter, PCMK__XE_LRM_RESOURCES);
+ iter = pcmk__xe_create(iter, PCMK__XE_LRM_RESOURCE);
- crm_xml_add(iter, XML_ATTR_ID, op->rsc_id);
+ crm_xml_add(iter, PCMK_XA_ID, op->rsc_id);
controld_add_resource_history_xml(iter, rsc, op,
controld_globals.our_nodename);
@@ -1771,7 +1783,7 @@ controld_ack_event_directly(const char *to_host, const char *to_sys,
crm_debug("ACK'ing resource op " PCMK__OP_FMT " from %s: %s",
op->rsc_id, op->op_type, op->interval_ms, op->user_data,
- crm_element_value(reply, XML_ATTR_REFERENCE));
+ crm_element_value(reply, PCMK_XA_REFERENCE));
if (relay_message(reply, TRUE) == FALSE) {
crm_log_xml_err(reply, "Unable to route reply");
@@ -1916,10 +1928,10 @@ do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc, xmlNode *msg,
CRM_CHECK((rsc != NULL) && (msg != NULL), return);
- operation = crm_element_value(msg, XML_LRM_ATTR_TASK);
+ operation = crm_element_value(msg, PCMK_XA_OPERATION);
CRM_CHECK(!pcmk__str_empty(operation), return);
- transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY);
+ transition = crm_element_value(msg, PCMK__XA_TRANSITION_KEY);
if (pcmk__str_empty(transition)) {
crm_log_xml_err(msg, "Missing transition number");
}
@@ -1982,8 +1994,8 @@ do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc, xmlNode *msg,
crm_notice("Discarding attempt to perform action %s on %s in state %s "
"(shutdown=%s)", operation, rsc->id,
fsa_state2string(controld_globals.fsa_state),
- pcmk__btoa(pcmk_is_set(controld_globals.fsa_input_register,
- R_SHUTDOWN)));
+ pcmk__flag_text(controld_globals.fsa_input_register,
+ R_SHUTDOWN));
lrmd__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_INVALID,
nack_reason);
@@ -2013,17 +2025,17 @@ do_lrm_rsc_op(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc, xmlNode *msg,
char *call_id_s = make_stop_id(rsc->id, call_id);
active_op_t *pending = NULL;
- pending = calloc(1, sizeof(active_op_t));
+ pending = pcmk__assert_alloc(1, sizeof(active_op_t));
crm_trace("Recording pending op: %d - %s %s", call_id, op_id, call_id_s);
pending->call_id = call_id;
pending->interval_ms = op->interval_ms;
- pending->op_type = strdup(operation);
- pending->op_key = strdup(op_id);
- pending->rsc_id = strdup(rsc->id);
+ pending->op_type = pcmk__str_copy(operation);
+ pending->op_key = pcmk__str_copy(op_id);
+ pending->rsc_id = pcmk__str_copy(rsc->id);
pending->start_time = time(NULL);
- pcmk__str_update(&pending->user_data, op->user_data);
- if (crm_element_value_epoch(msg, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
+ pending->user_data = pcmk__str_copy(op->user_data);
+ if (crm_element_value_epoch(msg, PCMK_OPT_SHUTDOWN_LOCK,
&(pending->lock_time)) != pcmk_ok) {
pending->lock_time = 0;
}
@@ -2082,7 +2094,7 @@ unescape_newlines(const char *string)
return NULL;
}
- ret = strdup(string);
+ ret = pcmk__str_copy(string);
pch = strstr(ret, escaped_newline);
while (pch != NULL) {
/* Replace newline escape pattern with actual newline (and a space so we
@@ -2249,11 +2261,12 @@ process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
rsc = lrm_state_get_rsc_info(lrm_state, op->rsc_id, 0);
}
if ((rsc == NULL) && action_xml) {
- xmlNode *xml = find_xml_node(action_xml, XML_CIB_TAG_RESOURCE, TRUE);
+ xmlNode *xml = pcmk__xe_first_child(action_xml, PCMK_XE_PRIMITIVE, NULL,
+ NULL);
- const char *standard = crm_element_value(xml, XML_AGENT_ATTR_CLASS);
- const char *provider = crm_element_value(xml, XML_AGENT_ATTR_PROVIDER);
- const char *type = crm_element_value(xml, XML_ATTR_TYPE);
+ const char *standard = crm_element_value(xml, PCMK_XA_CLASS);
+ const char *provider = crm_element_value(xml, PCMK_XA_PROVIDER);
+ const char *type = crm_element_value(xml, PCMK_XA_TYPE);
if (standard && type) {
crm_info("%s agent information not cached, using %s%s%s:%s from action XML",
@@ -2270,7 +2283,7 @@ process_lrm_event(lrm_state_t *lrm_state, lrmd_event_data_t *op,
if (lrm_state) {
node_name = lrm_state->node_name;
} else if (action_xml) {
- node_name = crm_element_value(action_xml, XML_LRM_ATTR_TARGET);
+ node_name = crm_element_value(action_xml, PCMK__META_ON_NODE);
}
if(pending == NULL) {
diff --git a/daemons/controld/controld_execd_state.c b/daemons/controld/controld_execd_state.c
index b90cc5e..1919684 100644
--- a/daemons/controld/controld_execd_state.c
+++ b/daemons/controld/controld_execd_state.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.
*
@@ -12,8 +12,8 @@
#include <errno.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/iso8601.h>
+#include <crm/common/xml.h>
#include <crm/pengine/rules.h>
#include <crm/pengine/rules_internal.h>
#include <crm/lrmd_internal.h>
@@ -116,12 +116,9 @@ lrm_state_create(const char *node_name)
return NULL;
}
- state = calloc(1, sizeof(lrm_state_t));
- if (!state) {
- return NULL;
- }
+ state = pcmk__assert_alloc(1, sizeof(lrm_state_t));
- state->node_name = strdup(node_name);
+ state->node_name = pcmk__str_copy(node_name);
state->rsc_info_cache = pcmk__strkey_table(NULL, free_rsc_info);
state->deletion_ops = pcmk__strkey_table(free, free_deletion_op);
state->active_ops = pcmk__strkey_table(free, free_recurring_op);
@@ -453,7 +450,7 @@ crmd_proxy_dispatch(const char *session, xmlNode *msg)
{
crm_trace("Processing proxied IPC message from session %s", session);
crm_log_xml_trace(msg, "controller[inbound]");
- crm_xml_add(msg, F_CRM_SYS_FROM, session);
+ crm_xml_add(msg, PCMK__XA_CRM_SYS_FROM, session);
if (controld_authorize_ipc_message(msg, NULL, session)) {
route_message(C_IPC_MESSAGE, msg);
}
@@ -477,8 +474,9 @@ remote_config_check(xmlNode * msg, int call_id, int rc, xmlNode * output, void *
crm_debug("Call %d : Parsing CIB options", call_id);
- pe_unpack_nvpairs(output, output, XML_CIB_TAG_PROPSET, NULL,
- config_hash, CIB_OPTIONS_FIRST, FALSE, now, NULL);
+ pe_unpack_nvpairs(output, output, PCMK_XE_CLUSTER_PROPERTY_SET, NULL,
+ config_hash, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, FALSE,
+ now, NULL);
/* Now send it to the remote peer */
lrmd__validate_remote_settings(lrmd, config_hash);
@@ -492,20 +490,22 @@ static void
crmd_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg)
{
lrm_state_t *lrm_state = userdata;
- const char *session = crm_element_value(msg, F_LRMD_IPC_SESSION);
+ const char *session = crm_element_value(msg, PCMK__XA_LRMD_IPC_SESSION);
remote_proxy_t *proxy = g_hash_table_lookup(proxy_table, session);
- const char *op = crm_element_value(msg, F_LRMD_IPC_OP);
+ const char *op = crm_element_value(msg, PCMK__XA_LRMD_IPC_OP);
if (pcmk__str_eq(op, LRMD_IPC_OP_NEW, pcmk__str_casei)) {
- const char *channel = crm_element_value(msg, F_LRMD_IPC_IPC_SERVER);
+ const char *channel = crm_element_value(msg, PCMK__XA_LRMD_IPC_SERVER);
proxy = crmd_remote_proxy_new(lrmd, lrm_state->node_name, session, channel);
if (!remote_ra_controlling_guest(lrm_state)) {
if (proxy != NULL) {
cib_t *cib_conn = controld_globals.cib_conn;
- /* Look up stonith-watchdog-timeout and send to the remote peer for validation */
- int rc = cib_conn->cmds->query(cib_conn, XML_CIB_TAG_CRMCONFIG,
+ /* Look up PCMK_OPT_STONITH_WATCHDOG_TIMEOUT and send to the
+ * remote peer for validation
+ */
+ int rc = cib_conn->cmds->query(cib_conn, PCMK_XE_CRM_CONFIG,
NULL, cib_scope_local);
cib_conn->cmds->register_callback_full(cib_conn, rc, 10, FALSE,
lrmd,
@@ -525,7 +525,8 @@ crmd_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg)
if (!remote_ra_is_in_maintenance(lrm_state)) {
now_s = pcmk__ttoa(time(NULL));
- update_attrd(lrm_state->node_name, XML_CIB_ATTR_SHUTDOWN, now_s, NULL, TRUE);
+ update_attrd(lrm_state->node_name, PCMK__NODE_ATTR_SHUTDOWN, now_s,
+ NULL, TRUE);
free(now_s);
remote_proxy_ack_shutdown(lrmd);
@@ -545,39 +546,43 @@ crmd_remote_proxy_cb(lrmd_t *lrmd, void *userdata, xmlNode *msg)
* to send to ourselves over IPC -- do it directly.
*/
int flags = 0;
- xmlNode *request = get_message_xml(msg, F_LRMD_IPC_MSG);
+ 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);
CRM_CHECK(lrm_state->node_name, return);
- crm_xml_add(request, XML_ACL_TAG_ROLE, "pacemaker-remote");
- pcmk__update_acl_user(request, F_LRMD_IPC_USER, lrm_state->node_name);
+ crm_xml_add(request, PCMK_XE_ACL_ROLE, "pacemaker-remote");
+ pcmk__update_acl_user(request, PCMK__XA_LRMD_IPC_USER,
+ lrm_state->node_name);
/* Pacemaker Remote nodes don't know their own names (as known to the
* cluster). When getting a node info request with no name or ID, add
* the name, so we don't return info for ourselves instead of the
* Pacemaker Remote node.
*/
- if (pcmk__str_eq(crm_element_value(request, F_CRM_TASK), CRM_OP_NODE_INFO, pcmk__str_casei)) {
+ if (pcmk__str_eq(crm_element_value(request, PCMK__XA_CRM_TASK),
+ CRM_OP_NODE_INFO, pcmk__str_none)) {
int node_id = 0;
- crm_element_value_int(request, XML_ATTR_ID, &node_id);
+ crm_element_value_int(request, PCMK_XA_ID, &node_id);
if ((node_id <= 0)
- && (crm_element_value(request, XML_ATTR_UNAME) == NULL)) {
- crm_xml_add(request, XML_ATTR_UNAME, lrm_state->node_name);
+ && (crm_element_value(request, PCMK_XA_UNAME) == NULL)) {
+ crm_xml_add(request, PCMK_XA_UNAME, lrm_state->node_name);
}
}
crmd_proxy_dispatch(session, request);
- crm_element_value_int(msg, F_LRMD_IPC_MSG_FLAGS, &flags);
+ crm_element_value_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, &flags);
if (flags & crm_ipc_client_response) {
int msg_id = 0;
- xmlNode *op_reply = create_xml_node(NULL, "ack");
+ xmlNode *op_reply = pcmk__xe_create(NULL, PCMK__XE_ACK);
- crm_xml_add(op_reply, "function", __func__);
- crm_xml_add_int(op_reply, "line", __LINE__);
+ crm_xml_add(op_reply, PCMK_XA_FUNCTION, __func__);
+ crm_xml_add_int(op_reply, PCMK__XA_LINE, __LINE__);
- 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);
remote_proxy_relay_response(proxy, op_reply, msg_id);
free_xml(op_reply);
@@ -650,7 +655,7 @@ lrm_state_get_metadata(lrm_state_t * lrm_state,
* @TODO Make meta-data calls asynchronous. (This will be part of a larger
* project to make meta-data calls via the executor rather than directly.)
*/
- params = lrmd_key_value_add(params, CRM_META "_" XML_LRM_ATTR_TARGET,
+ params = lrmd_key_value_add(params, CRM_META "_" PCMK__META_ON_NODE,
lrm_state->node_name);
return ((lrmd_t *) lrm_state->conn)->cmds->get_metadata_params(lrm_state->conn,
diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
index 9557d9e..dcffc8e 100644
--- a/daemons/controld/controld_fencing.c
+++ b/daemons/controld/controld_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.
*
@@ -9,7 +9,6 @@
#include <crm_internal.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
@@ -63,9 +62,9 @@ set_fence_reaction(const char *reaction_s)
fence_reaction_panic = true;
} else {
- if (!pcmk__str_eq(reaction_s, "stop", pcmk__str_casei)) {
+ if (!pcmk__str_eq(reaction_s, PCMK_VALUE_STOP, pcmk__str_casei)) {
crm_warn("Invalid value '%s' for %s, using 'stop'",
- reaction_s, XML_CONFIG_ATTR_FENCE_REACTION);
+ reaction_s, PCMK_OPT_FENCE_REACTION);
}
fence_reaction_panic = false;
}
@@ -82,10 +81,10 @@ controld_configure_fencing(GHashTable *options)
{
const char *value = NULL;
- value = g_hash_table_lookup(options, XML_CONFIG_ATTR_FENCE_REACTION);
+ value = g_hash_table_lookup(options, PCMK_OPT_FENCE_REACTION);
set_fence_reaction(value);
- value = g_hash_table_lookup(options, "stonith-max-attempts");
+ value = g_hash_table_lookup(options, PCMK_OPT_STONITH_MAX_ATTEMPTS);
update_stonith_max_attempts(value);
}
@@ -176,7 +175,7 @@ st_fail_count_increment(const char *target)
}
rec->count = 1;
- g_hash_table_insert(stonith_failures, strdup(target), rec);
+ g_hash_table_insert(stonith_failures, pcmk__str_copy(target), rec);
}
}
@@ -191,8 +190,8 @@ cib_fencing_updated(xmlNode *msg, int call_id, int rc, xmlNode *output,
crm_err("Fencing update %d for %s: failed - %s (%d)",
call_id, (char *)user_data, pcmk_strerror(rc), rc);
crm_log_xml_warn(msg, "Failed update");
- abort_transition(INFINITY, pcmk__graph_shutdown, "CIB update failed",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_shutdown,
+ "CIB update failed", NULL);
} else {
crm_info("Fencing update %d for %s: complete", call_id, (char *)user_data);
@@ -222,7 +221,7 @@ send_stonith_update(pcmk__graph_action_t *action, const char *target,
* Try getting any existing node cache entry also by node uuid in case it
* doesn't have an uname yet.
*/
- peer = pcmk__get_peer_full(0, target, uuid, CRM_GET_PEER_ANY);
+ peer = pcmk__get_node(0, target, uuid, pcmk__node_search_any);
CRM_CHECK(peer != NULL, return);
@@ -236,7 +235,7 @@ send_stonith_update(pcmk__graph_action_t *action, const char *target,
if (peer->uuid == NULL) {
crm_info("Recording uuid '%s' for node '%s'", uuid, target);
- peer->uuid = strdup(uuid);
+ peer->uuid = pcmk__str_copy(uuid);
}
crmd_peer_down(peer, TRUE);
@@ -248,21 +247,21 @@ send_stonith_update(pcmk__graph_action_t *action, const char *target,
if (peer->flags & crm_remote_node) {
char *now_s = pcmk__ttoa(time(NULL));
- crm_xml_add(node_state, XML_NODE_IS_FENCED, now_s);
+ crm_xml_add(node_state, PCMK__XA_NODE_FENCED, now_s);
free(now_s);
}
/* Force our known ID */
- crm_xml_add(node_state, XML_ATTR_ID, uuid);
+ crm_xml_add(node_state, PCMK_XA_ID, uuid);
rc = controld_globals.cib_conn->cmds->modify(controld_globals.cib_conn,
- XML_CIB_TAG_STATUS, node_state,
+ PCMK_XE_STATUS, node_state,
cib_scope_local
|cib_can_create);
/* Delay processing the trigger until the update completes */
crm_debug("Sending fencing update %d for %s", rc, target);
- fsa_register_cib_callback(rc, strdup(target), cib_fencing_updated);
+ fsa_register_cib_callback(rc, pcmk__str_copy(target), cib_fencing_updated);
// Make sure it sticks
/* controld_globals.cib_conn->cmds->bump_epoch(controld_globals.cib_conn,
@@ -293,7 +292,8 @@ abort_for_stonith_failure(enum pcmk__graph_next abort_action,
if ((abort_action != pcmk__graph_wait) && too_many_st_failures(target)) {
abort_action = pcmk__graph_wait;
}
- abort_transition(INFINITY, abort_action, "Stonith failed", reason);
+ abort_transition(PCMK_SCORE_INFINITY, abort_action, "Stonith failed",
+ reason);
}
@@ -315,7 +315,8 @@ static GList *stonith_cleanup_list = NULL;
*/
void
add_stonith_cleanup(const char *target) {
- stonith_cleanup_list = g_list_append(stonith_cleanup_list, strdup(target));
+ stonith_cleanup_list = g_list_append(stonith_cleanup_list,
+ pcmk__str_copy(target));
}
/*!
@@ -374,8 +375,9 @@ execute_stonith_cleanup(void)
for (iter = stonith_cleanup_list; iter != NULL; iter = iter->next) {
char *target = iter->data;
- crm_node_t *target_node = crm_get_peer(0, target);
- const char *uuid = crm_peer_uuid(target_node);
+ crm_node_t *target_node =
+ pcmk__get_node(0, target, NULL, pcmk__node_search_cluster_member);
+ const char *uuid = pcmk__cluster_node_uuid(target_node);
crm_notice("Marking %s, target of a previous stonith action, as clean", target);
send_stonith_update(NULL, target, uuid);
@@ -424,13 +426,13 @@ fail_incompletable_stonith(pcmk__graph_t *graph)
continue;
}
- task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
+ task = crm_element_value(action->xml, PCMK_XA_OPERATION);
if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) {
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
last_action = action->xml;
pcmk__update_graph(graph, action);
crm_notice("Failing action %d (%s): fencer terminated",
- action->id, ID(action->xml));
+ action->id, pcmk__xe_id(action->xml));
}
}
}
@@ -580,15 +582,17 @@ handle_fence_notification(stonith_t *st, stonith_event_t *event)
event->id);
if (succeeded) {
- crm_node_t *peer = pcmk__search_known_node_cache(0, event->target,
- CRM_GET_PEER_ANY);
+ const uint32_t flags = pcmk__node_search_any
+ |pcmk__node_search_cluster_cib;
+
+ crm_node_t *peer = pcmk__search_node_caches(0, event->target, flags);
const char *uuid = NULL;
if (peer == NULL) {
return;
}
- uuid = crm_peer_uuid(peer);
+ uuid = pcmk__cluster_node_uuid(peer);
if (AM_I_DC) {
/* The DC always sends updates */
@@ -598,8 +602,8 @@ handle_fence_notification(stonith_t *st, stonith_event_t *event)
* hosted any guest nodes, and call remote_node_down() for them.
* Unfortunately, the controller doesn't have a simple, reliable way
* to map hosts to guests. It might be possible to track this in the
- * peer cache via crm_remote_peer_cache_refresh(). For now, we rely
- * on the scheduler creating fence pseudo-events for the guests.
+ * peer cache via refresh_remote_nodes(). For now, we rely on the
+ * scheduler creating fence pseudo-events for the guests.
*/
if (!pcmk__str_eq(client, te_client_id, pcmk__str_casei)) {
@@ -608,7 +612,7 @@ handle_fence_notification(stonith_t *st, stonith_event_t *event)
*/
crm_info("External fencing operation from %s fenced %s",
client, event->target);
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"External Fencing Operation", NULL);
}
@@ -713,15 +717,16 @@ controld_timer_fencer_connect(gpointer user_data)
}
if (rc == pcmk_ok) {
- stonith_api->cmds->register_notification(stonith_api,
- T_STONITH_NOTIFY_DISCONNECT,
- tengine_stonith_connection_destroy);
- stonith_api->cmds->register_notification(stonith_api,
- T_STONITH_NOTIFY_FENCE,
- handle_fence_notification);
- stonith_api->cmds->register_notification(stonith_api,
- T_STONITH_NOTIFY_HISTORY_SYNCED,
- tengine_stonith_history_synced);
+ stonith_api_operations_t *cmds = stonith_api->cmds;
+
+ cmds->register_notification(stonith_api,
+ PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ tengine_stonith_connection_destroy);
+ cmds->register_notification(stonith_api, PCMK__VALUE_ST_NOTIFY_FENCE,
+ handle_fence_notification);
+ cmds->register_notification(stonith_api,
+ PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED,
+ tengine_stonith_history_synced);
te_trigger_stonith_history_sync(TRUE);
crm_notice("Fencer successfully connected");
}
@@ -829,7 +834,7 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
goto bail;
}
- target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ target = crm_element_value(action->xml, PCMK__META_ON_NODE);
if (target == NULL) {
crm_err("Ignoring fence operation %d result: No target given (bug?)",
data->call_id);
@@ -838,8 +843,10 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
stop_te_timer(action);
if (stonith__exit_status(data) == CRM_EX_OK) {
- const char *uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
- const char *op = crm_meta_value(action->params, "stonith_action");
+ const char *uuid = crm_element_value(action->xml,
+ PCMK__META_ON_NODE_UUID);
+ const char *op = crm_meta_value(action->params,
+ PCMK__META_STONITH_ACTION);
crm_info("Fence operation %d for %s succeeded", data->call_id, target);
if (!(pcmk_is_set(action->flags, pcmk__graph_action_confirmed))) {
@@ -864,11 +871,12 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
is_remote_node);
free(now);
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
+ value = crm_meta_value(action->params, PCMK__META_DIGESTS_ALL);
update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL,
is_remote_node);
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
+ value = crm_meta_value(action->params,
+ PCMK__META_DIGESTS_SECURE);
update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL,
is_remote_node);
@@ -952,10 +960,11 @@ controld_execute_fence_action(pcmk__graph_t *graph,
pcmk__graph_action_t *action)
{
int rc = 0;
- const char *id = ID(action->xml);
- const char *uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
- const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
- const char *type = crm_meta_value(action->params, "stonith_action");
+ const char *id = pcmk__xe_id(action->xml);
+ const char *uuid = crm_element_value(action->xml, PCMK__META_ON_NODE_UUID);
+ const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
+ const char *type = crm_meta_value(action->params,
+ PCMK__META_STONITH_ACTION);
char *transition_key = NULL;
const char *priority_delay = NULL;
int delay_i = 0;
@@ -973,7 +982,8 @@ controld_execute_fence_action(pcmk__graph_t *graph,
return EPROTO;
}
- priority_delay = crm_meta_value(action->params, XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY);
+ priority_delay = crm_meta_value(action->params,
+ PCMK_OPT_PRIORITY_FENCING_DELAY);
crm_notice("Requesting fencing (%s) targeting node %s "
CRM_XS " action=%s timeout=%i%s%s",
@@ -1001,17 +1011,16 @@ controld_execute_fence_action(pcmk__graph_t *graph,
bool
controld_verify_stonith_watchdog_timeout(const char *value)
{
- long st_timeout = value? crm_get_msec(value) : 0;
+ long long st_timeout = (value != NULL)? crm_get_msec(value) : 0;
const char *our_nodename = controld_globals.our_nodename;
- gboolean rv = TRUE;
if (st_timeout == 0
|| (stonith_api && (stonith_api->state != stonith_disconnected) &&
stonith__watchdog_fencing_enabled_for_node_api(stonith_api,
our_nodename))) {
- rv = pcmk__valid_sbd_timeout(value);
+ return pcmk__valid_stonith_watchdog_timeout(value);
}
- return rv;
+ return true;
}
/* end stonith API client functions */
@@ -1042,7 +1051,7 @@ te_cleanup_stonith_history_sync(stonith_t *st, bool free_timers)
}
if (st) {
- st->cmds->remove_notification(st, T_STONITH_NOTIFY_HISTORY_SYNCED);
+ st->cmds->remove_notification(st, PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED);
}
}
diff --git a/daemons/controld/controld_fsa.c b/daemons/controld/controld_fsa.c
index 06559b8..79b3507 100644
--- a/daemons/controld/controld_fsa.c
+++ b/daemons/controld/controld_fsa.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.
*
@@ -18,7 +18,6 @@
#include <crm/crm.h>
#include <crm/lrmd.h>
#include <crm/cib.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster/election_internal.h>
#include <crm/cluster.h>
@@ -198,7 +197,7 @@ s_crmd_fsa(enum crmd_fsa_cause cause)
if ((controld_globals.fsa_message_queue == NULL)
&& (controld_globals.fsa_actions != A_NOTHING)) {
/* fake the first message so we can get into the loop */
- fsa_data = calloc(1, sizeof(fsa_data_t));
+ fsa_data = pcmk__assert_alloc(1, sizeof(fsa_data_t));
fsa_data->fsa_input = I_NULL;
fsa_data->fsa_cause = C_FSA_INTERNAL;
fsa_data->origin = __func__;
@@ -283,8 +282,8 @@ s_crmd_fsa(enum crmd_fsa_cause cause)
crm_debug("Exiting the FSA: queue=%d, fsa_actions=%#llx, stalled=%s",
g_list_length(controld_globals.fsa_message_queue),
(unsigned long long) controld_globals.fsa_actions,
- pcmk__btoa(pcmk_is_set(controld_globals.flags,
- controld_fsa_is_stalled)));
+ pcmk__flag_text(controld_globals.flags,
+ controld_fsa_is_stalled));
} else {
crm_trace("Exiting the FSA");
}
@@ -549,7 +548,7 @@ check_join_counts(fsa_data_t *msg_data)
return;
}
- npeers = crm_active_peers();
+ npeers = pcmk__cluster_num_active_nodes();
count = crmd_join_phase_count(crm_join_confirmed);
if (count == npeers) {
if (npeers == 1) {
diff --git a/daemons/controld/controld_fsa.h b/daemons/controld/controld_fsa.h
index 2b79f07..ad1c4fa 100644
--- a/daemons/controld/controld_fsa.h
+++ b/daemons/controld/controld_fsa.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -401,7 +401,9 @@ enum crmd_fsa_input {
# define R_HAVE_CIB 0x00020000ULL /* Do we have an up-to-date CIB */
# define R_MEMBERSHIP 0x00100000ULL /* Have we got cluster layer data yet */
-# define R_PEER_DATA 0x00200000ULL /* Have we got T_CL_STATUS data yet */
+
+// Ever received membership-layer data
+# define R_PEER_DATA 0x00200000ULL
# define R_HA_DISCONNECTED 0x00400000ULL /* did we sign out of our own accord */
diff --git a/daemons/controld/controld_join_client.c b/daemons/controld/controld_join_client.c
index 805ecbd..8faf58b 100644
--- a/daemons/controld/controld_join_client.c
+++ b/daemons/controld/controld_join_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.
*
@@ -11,7 +11,6 @@
#include <crm/crm.h>
#include <crm/cib.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <pacemaker-controld.h>
@@ -34,8 +33,9 @@ static void
update_dc_expected(const xmlNode *msg)
{
if ((controld_globals.dc_name != NULL)
- && pcmk__xe_attr_is_true(msg, F_CRM_DC_LEAVING)) {
- crm_node_t *dc_node = crm_get_peer(0, controld_globals.dc_name);
+ && pcmk__xe_attr_is_true(msg, PCMK__XA_DC_LEAVING)) {
+ crm_node_t *dc_node = pcmk__get_node(0, controld_globals.dc_name, NULL,
+ pcmk__node_search_cluster_member);
pcmk__update_peer_expected(__func__, dc_node, CRMD_JOINSTATE_DOWN);
}
@@ -55,7 +55,7 @@ do_cl_join_query(long long action,
sleep(1); // Give the cluster layer time to propagate to the DC
update_dc(NULL); /* Unset any existing value so that the result is not discarded */
crm_debug("Querying for a DC");
- send_cluster_message(NULL, crm_msg_crmd, req, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_crmd, req);
free_xml(req);
}
@@ -84,7 +84,7 @@ do_cl_join_announce(long long action,
crm_debug("Announcing availability");
update_dc(NULL);
- send_cluster_message(NULL, crm_msg_crmd, req, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_crmd, req);
free_xml(req);
} else {
@@ -112,10 +112,10 @@ do_cl_join_offer_respond(long long action,
CRM_CHECK(input != NULL, return);
- welcome_from = crm_element_value(input->msg, F_CRM_HOST_FROM);
- join_id = crm_element_value(input->msg, F_CRM_JOIN_ID);
+ welcome_from = crm_element_value(input->msg, PCMK__XA_SRC);
+ join_id = crm_element_value(input->msg, PCMK__XA_JOIN_ID);
crm_trace("Accepting cluster join offer from node %s "CRM_XS" join-%s",
- welcome_from, crm_element_value(input->msg, F_CRM_JOIN_ID));
+ welcome_from, crm_element_value(input->msg, PCMK__XA_JOIN_ID));
/* we only ever want the last one */
if (query_call_id > 0) {
@@ -134,7 +134,7 @@ do_cl_join_offer_respond(long long action,
query_call_id = cib_conn->cmds->query(cib_conn, NULL, NULL,
cib_scope_local|cib_no_children);
- fsa_register_cib_callback(query_call_id, strdup(join_id),
+ fsa_register_cib_callback(query_call_id, pcmk__str_copy(join_id),
join_query_callback);
crm_trace("Registered join query callback: %d", query_call_id);
@@ -146,7 +146,7 @@ void
join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
char *join_id = user_data;
- xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);
+ xmlNode *generation = pcmk__xe_create(NULL, PCMK__XE_GENERATION_TUPLE);
CRM_LOG_ASSERT(join_id != NULL);
@@ -166,19 +166,21 @@ join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *
} else {
xmlNode *reply = NULL;
+ const crm_node_t *dc_node =
+ pcmk__get_node(0, controld_globals.dc_name, NULL,
+ pcmk__node_search_cluster_member);
crm_debug("Respond to join offer join-%s from %s",
join_id, controld_globals.dc_name);
- copy_in_properties(generation, output);
+ pcmk__xe_copy_attrs(generation, output, pcmk__xaf_none);
reply = create_request(CRM_OP_JOIN_REQUEST, generation,
controld_globals.dc_name, CRM_SYSTEM_DC,
CRM_SYSTEM_CRMD, NULL);
- crm_xml_add(reply, F_CRM_JOIN_ID, join_id);
- crm_xml_add(reply, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
- send_cluster_message(crm_get_peer(0, controld_globals.dc_name),
- crm_msg_crmd, reply, TRUE);
+ crm_xml_add(reply, PCMK__XA_JOIN_ID, join_id);
+ crm_xml_add(reply, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
+ pcmk__cluster_send_message(dc_node, crm_msg_crmd, reply);
free_xml(reply);
}
@@ -190,29 +192,32 @@ void
set_join_state(const char *start_state, const char *node_name, const char *node_uuid,
bool remote)
{
- if (pcmk__str_eq(start_state, "standby", pcmk__str_casei)) {
+ if (pcmk__str_eq(start_state, PCMK_VALUE_STANDBY, pcmk__str_casei)) {
crm_notice("Forcing node %s to join in %s state per configured "
"environment", node_name, start_state);
cib__update_node_attr(controld_globals.logger_out,
controld_globals.cib_conn, cib_sync_call,
- XML_CIB_TAG_NODES, node_uuid,
- NULL, NULL, NULL, "standby", "on", NULL,
- remote ? "remote" : NULL);
+ PCMK_XE_NODES, node_uuid,
+ NULL, NULL, NULL, PCMK_NODE_ATTR_STANDBY,
+ PCMK_VALUE_TRUE, NULL,
+ (remote? PCMK_VALUE_REMOTE : NULL));
- } else if (pcmk__str_eq(start_state, "online", pcmk__str_casei)) {
+ } else if (pcmk__str_eq(start_state, PCMK_VALUE_ONLINE, pcmk__str_casei)) {
crm_notice("Forcing node %s to join in %s state per configured "
"environment", node_name, start_state);
cib__update_node_attr(controld_globals.logger_out,
controld_globals.cib_conn, cib_sync_call,
- XML_CIB_TAG_NODES, node_uuid,
- NULL, NULL, NULL, "standby", "off", NULL,
- remote ? "remote" : NULL);
+ PCMK_XE_NODES, node_uuid,
+ NULL, NULL, NULL, PCMK_NODE_ATTR_STANDBY,
+ PCMK_VALUE_FALSE, NULL,
+ (remote? PCMK_VALUE_REMOTE : NULL));
- } else if (pcmk__str_eq(start_state, "default", pcmk__str_casei)) {
+ } else if (pcmk__str_eq(start_state, PCMK_VALUE_DEFAULT, pcmk__str_casei)) {
crm_debug("Not forcing a starting state on node %s", node_name);
} else {
- crm_warn("Unrecognized start state '%s', using 'default' (%s)",
+ crm_warn("Unrecognized start state '%s', using "
+ "'" PCMK_VALUE_DEFAULT "' (%s)",
start_state, node_name);
}
}
@@ -220,11 +225,11 @@ set_join_state(const char *start_state, const char *node_name, const char *node_
static int
update_conn_host_cache(xmlNode *node, void *userdata)
{
- const char *remote = crm_element_value(node, XML_ATTR_ID);
- const char *conn_host = crm_element_value(node, PCMK__XA_CONN_HOST);
- const char *state = crm_element_value(node, XML_CIB_TAG_STATE);
+ const char *remote = crm_element_value(node, PCMK_XA_ID);
+ const char *conn_host = crm_element_value(node, PCMK__XA_CONNECTION_HOST);
+ const char *state = crm_element_value(node, PCMK__XA_NODE_STATE);
- crm_node_t *remote_peer = crm_remote_peer_get(remote);
+ crm_node_t *remote_peer = pcmk__cluster_lookup_remote_node(remote);
if (remote_peer == NULL) {
return pcmk_rc_ok;
@@ -256,8 +261,8 @@ do_cl_join_finalize_respond(long long action,
const char *start_state = pcmk__env_option(PCMK__ENV_NODE_START_STATE);
int join_id = -1;
- const char *op = crm_element_value(input->msg, F_CRM_TASK);
- const char *welcome_from = crm_element_value(input->msg, F_CRM_HOST_FROM);
+ const char *op = crm_element_value(input->msg, PCMK__XA_CRM_TASK);
+ const char *welcome_from = crm_element_value(input->msg, PCMK__XA_SRC);
if (!pcmk__str_eq(op, CRM_OP_JOIN_ACKNAK, pcmk__str_casei)) {
crm_trace("Ignoring op=%s message", op);
@@ -269,7 +274,7 @@ do_cl_join_finalize_respond(long long action,
was_nack = FALSE;
}
- crm_element_value_int(input->msg, F_CRM_JOIN_ID, &join_id);
+ crm_element_value_int(input->msg, PCMK__XA_JOIN_ID, &join_id);
if (was_nack) {
crm_err("Shutting down because cluster join with leader %s failed "
@@ -305,8 +310,11 @@ do_cl_join_finalize_respond(long long action,
xmlNode *reply = create_request(CRM_OP_JOIN_CONFIRM, tmp1,
controld_globals.dc_name, CRM_SYSTEM_DC,
CRM_SYSTEM_CRMD, NULL);
+ const crm_node_t *dc_node =
+ pcmk__get_node(0, controld_globals.dc_name, NULL,
+ pcmk__node_search_cluster_member);
- crm_xml_add_int(reply, F_CRM_JOIN_ID, join_id);
+ crm_xml_add_int(reply, PCMK__XA_JOIN_ID, join_id);
crm_debug("Confirming join-%d: sending local operation history to %s",
join_id, controld_globals.dc_name);
@@ -333,8 +341,7 @@ do_cl_join_finalize_respond(long long action,
}
}
- send_cluster_message(crm_get_peer(0, controld_globals.dc_name),
- crm_msg_crmd, reply, TRUE);
+ pcmk__cluster_send_message(dc_node, crm_msg_crmd, reply);
free_xml(reply);
if (AM_I_DC == FALSE) {
@@ -347,9 +354,10 @@ do_cl_join_finalize_respond(long long action,
/* Update the remote node cache with information about which node
* is hosting the connection.
*/
- remotes = pcmk__xe_match(input->msg, XML_CIB_TAG_NODES, NULL, NULL);
+ remotes = pcmk__xe_first_child(input->msg, PCMK_XE_NODES, NULL, NULL);
if (remotes != NULL) {
- pcmk__xe_foreach_child(remotes, XML_CIB_TAG_NODE, update_conn_host_cache, NULL);
+ pcmk__xe_foreach_child(remotes, PCMK_XE_NODE,
+ update_conn_host_cache, NULL);
}
} else {
diff --git a/daemons/controld/controld_join_dc.c b/daemons/controld/controld_join_dc.c
index 2fe6710..e943e65 100644
--- a/daemons/controld/controld_join_dc.c
+++ b/daemons/controld/controld_join_dc.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,6 @@
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
@@ -188,12 +187,12 @@ create_dc_message(const char *join_op, const char *host_to)
CRM_SYSTEM_DC, NULL);
/* Identify which election this is a part of */
- crm_xml_add_int(msg, F_CRM_JOIN_ID, current_join_id);
+ crm_xml_add_int(msg, PCMK__XA_JOIN_ID, current_join_id);
/* Add a field specifying whether the DC is shutting down. This keeps the
* joining node from fencing the old DC if it becomes the new DC.
*/
- pcmk__xe_set_bool_attr(msg, F_CRM_DC_LEAVING,
+ pcmk__xe_set_bool_attr(msg, PCMK__XA_DC_LEAVING,
pcmk_is_set(controld_globals.fsa_input_register,
R_SHUTDOWN));
return msg;
@@ -206,7 +205,7 @@ join_make_offer(gpointer key, gpointer value, gpointer user_data)
crm_node_t *member = (crm_node_t *)value;
CRM_ASSERT(member != NULL);
- if (crm_is_peer_active(member) == FALSE) {
+ if (!pcmk__cluster_is_node_active(member)) {
crm_info("Not making join-%d offer to inactive node %s",
current_join_id,
(member->uname? member->uname : "with unknown name"));
@@ -249,10 +248,10 @@ join_make_offer(gpointer key, gpointer value, gpointer user_data)
offer = create_dc_message(CRM_OP_JOIN_OFFER, member->uname);
// Advertise our feature set so the joining node can bail if not compatible
- crm_xml_add(offer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
+ crm_xml_add(offer, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
crm_info("Sending join-%d offer to %s", current_join_id, member->uname);
- send_cluster_message(member, crm_msg_crmd, offer, TRUE);
+ pcmk__cluster_send_message(member, crm_msg_crmd, offer);
free_xml(offer);
crm_update_peer_join(__func__, member, crm_join_welcomed);
@@ -313,12 +312,12 @@ do_dc_join_offer_one(long long action,
return;
}
- join_to = crm_element_value(welcome->msg, F_CRM_HOST_FROM);
+ join_to = crm_element_value(welcome->msg, PCMK__XA_SRC);
if (join_to == NULL) {
crm_err("Can't make join-%d offer to unknown node", current_join_id);
return;
}
- member = crm_get_peer(0, join_to);
+ member = pcmk__get_node(0, join_to, NULL, pcmk__node_search_cluster_member);
/* It is possible that a node will have been sick or starting up when the
* original offer was made. However, it will either re-announce itself in
@@ -332,14 +331,16 @@ do_dc_join_offer_one(long long action,
* well, to ensure the correct value for max_generation_from.
*/
if (strcasecmp(join_to, controld_globals.our_nodename) != 0) {
- member = crm_get_peer(0, controld_globals.our_nodename);
+ member = pcmk__get_node(0, controld_globals.our_nodename, NULL,
+ pcmk__node_search_cluster_member);
join_make_offer(NULL, member, NULL);
}
/* This was a genuine join request; cancel any existing transition and
* invoke the scheduler.
*/
- abort_transition(INFINITY, pcmk__graph_restart, "Node join", NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart, "Node join",
+ NULL);
count = crmd_join_phase_count(crm_join_welcomed);
crm_info("Waiting on join-%d requests from %d outstanding node%s",
@@ -386,19 +387,20 @@ do_dc_join_filter_offer(long long action,
gboolean ack_nack_bool = TRUE;
ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg);
- const char *join_from = crm_element_value(join_ack->msg, F_CRM_HOST_FROM);
- const char *ref = crm_element_value(join_ack->msg, F_CRM_REFERENCE);
+ const char *join_from = crm_element_value(join_ack->msg, PCMK__XA_SRC);
+ const char *ref = crm_element_value(join_ack->msg, PCMK_XA_REFERENCE);
const char *join_version = crm_element_value(join_ack->msg,
- XML_ATTR_CRM_VERSION);
+ PCMK_XA_CRM_FEATURE_SET);
crm_node_t *join_node = NULL;
if (join_from == NULL) {
crm_err("Ignoring invalid join request without node name");
return;
}
- join_node = crm_get_peer(0, join_from);
+ join_node = pcmk__get_node(0, join_from, NULL,
+ pcmk__node_search_cluster_member);
- crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id);
+ crm_element_value_int(join_ack->msg, PCMK__XA_JOIN_ID, &join_id);
if (join_id != current_join_id) {
crm_debug("Ignoring join-%d request from %s because we are on join-%d",
join_id, join_from, current_join_id);
@@ -411,13 +413,22 @@ do_dc_join_filter_offer(long long action,
int lpc = 0;
const char *attributes[] = {
- XML_ATTR_GENERATION_ADMIN,
- XML_ATTR_GENERATION,
- XML_ATTR_NUMUPDATES,
+ PCMK_XA_ADMIN_EPOCH,
+ PCMK_XA_EPOCH,
+ PCMK_XA_NUM_UPDATES,
};
- for (lpc = 0; cmp == 0 && lpc < PCMK__NELEM(attributes); lpc++) {
- cmp = compare_int_fields(max_generation_xml, generation, attributes[lpc]);
+ /* It's not obvious that join_ack->xml is the PCMK__XE_GENERATION_TUPLE
+ * element from the join client. The "if" guard is for clarity.
+ */
+ if (pcmk__xe_is(generation, PCMK__XE_GENERATION_TUPLE)) {
+ for (lpc = 0; cmp == 0 && lpc < PCMK__NELEM(attributes); lpc++) {
+ cmp = compare_int_fields(max_generation_xml, generation,
+ attributes[lpc]);
+ }
+
+ } else { // Should always be PCMK__XE_GENERATION_TUPLE
+ CRM_LOG_ASSERT(false);
}
}
@@ -431,7 +442,7 @@ do_dc_join_filter_offer(long long action,
join_id, join_from, value, ref);
ack_nack_bool = FALSE;
- } else if (!crm_is_peer_active(join_node)) {
+ } else if (!pcmk__cluster_is_node_active(join_node)) {
if (match_down_event(join_from) != NULL) {
/* The join request was received after the node was fenced or
* otherwise shutdown in a way that we're aware of. No need to log
@@ -463,20 +474,20 @@ do_dc_join_filter_offer(long long action,
} else if (max_generation_xml == NULL) {
const char *validation = crm_element_value(generation,
- XML_ATTR_VALIDATION);
+ PCMK_XA_VALIDATE_WITH);
- if (get_schema_version(validation) < 0) {
+ if (pcmk__get_schema(validation) == NULL) {
crm_err("Rejecting join-%d request from %s (with first CIB "
"generation) due to unknown schema version %s "
CRM_XS " ref=%s",
- join_id, join_from, validation, ref);
+ join_id, join_from, pcmk__s(validation, "(missing)"), ref);
ack_nack_bool = FALSE;
} else {
crm_debug("Accepting join-%d request from %s (with first CIB "
"generation) " CRM_XS " ref=%s",
join_id, join_from, ref);
- max_generation_xml = copy_xml(generation);
+ max_generation_xml = pcmk__xml_copy(NULL, generation);
pcmk__str_update(&max_generation_from, join_from);
}
@@ -485,13 +496,14 @@ do_dc_join_filter_offer(long long action,
&& pcmk__str_eq(join_from, controld_globals.our_nodename,
pcmk__str_casei))) {
const char *validation = crm_element_value(generation,
- XML_ATTR_VALIDATION);
+ PCMK_XA_VALIDATE_WITH);
- if (get_schema_version(validation) < 0) {
+ if (pcmk__get_schema(validation) == NULL) {
crm_err("Rejecting join-%d request from %s (with better CIB "
"generation than current best from %s) due to unknown "
"schema version %s " CRM_XS " ref=%s",
- join_id, join_from, max_generation_from, validation, ref);
+ join_id, join_from, max_generation_from,
+ pcmk__s(validation, "(missing)"), ref);
ack_nack_bool = FALSE;
} else {
@@ -502,7 +514,7 @@ do_dc_join_filter_offer(long long action,
crm_log_xml_debug(generation, "New max generation");
free_xml(max_generation_xml);
- max_generation_xml = copy_xml(join_ack->xml);
+ max_generation_xml = pcmk__xml_copy(NULL, join_ack->xml);
pcmk__str_update(&max_generation_from, join_from);
}
@@ -588,7 +600,7 @@ do_dc_join_finalize(long long action,
if (pcmk_is_set(controld_globals.fsa_input_register, R_HAVE_CIB)) {
// Send our CIB out to everyone
- pcmk__str_update(&sync_from, controld_globals.our_nodename);
+ sync_from = pcmk__str_copy(controld_globals.our_nodename);
crm_debug("Finalizing join-%d for %d node%s (sync'ing from local CIB)",
current_join_id, count_finalizable,
pcmk__plural_s(count_finalizable));
@@ -596,7 +608,7 @@ do_dc_join_finalize(long long action,
} else {
// Ask for the agreed best CIB
- pcmk__str_update(&sync_from, max_generation_from);
+ sync_from = pcmk__str_copy(max_generation_from);
crm_notice("Finalizing join-%d for %d node%s (sync'ing CIB from %s)",
current_join_id, count_finalizable,
pcmk__plural_s(count_finalizable), sync_from);
@@ -698,8 +710,8 @@ do_dc_join_ack(long long action,
int join_id = -1;
ha_msg_input_t *join_ack = fsa_typed_data(fsa_dt_ha_msg);
- const char *op = crm_element_value(join_ack->msg, F_CRM_TASK);
- char *join_from = crm_element_value_copy(join_ack->msg, F_CRM_HOST_FROM);
+ const char *op = crm_element_value(join_ack->msg, PCMK__XA_CRM_TASK);
+ char *join_from = crm_element_value_copy(join_ack->msg, PCMK__XA_SRC);
crm_node_t *peer = NULL;
enum controld_section_e section = controld_section_lrm;
@@ -726,13 +738,13 @@ do_dc_join_ack(long long action,
goto done;
}
- if (crm_element_value_int(join_ack->msg, F_CRM_JOIN_ID, &join_id) != 0) {
+ if (crm_element_value_int(join_ack->msg, PCMK__XA_JOIN_ID, &join_id) != 0) {
crm_warn("Ignoring join confirmation from %s without valid join ID",
join_from);
goto done;
}
- peer = crm_get_peer(0, join_from);
+ peer = pcmk__get_node(0, join_from, NULL, pcmk__node_search_cluster_member);
if (peer->join != crm_join_finalized) {
crm_info("Ignoring out-of-sequence join-%d confirmation from %s "
"(currently %s not %s)",
@@ -800,7 +812,7 @@ do_dc_join_ack(long long action,
join_from, current_join_id);
}
- rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, state,
+ rc = cib->cmds->modify(cib, PCMK_XE_STATUS, state,
cib_scope_local|cib_can_create|cib_transaction);
free_xml(execd_state);
if (rc != pcmk_ok) {
@@ -854,10 +866,10 @@ finalize_join_for(gpointer key, gpointer value, gpointer user_data)
* weren't known before
*/
crm_trace("Updating node name and UUID in CIB for %s", join_to);
- tmp1 = create_xml_node(NULL, XML_CIB_TAG_NODE);
- crm_xml_add(tmp1, XML_ATTR_ID, crm_peer_uuid(join_node));
- crm_xml_add(tmp1, XML_ATTR_UNAME, join_to);
- fsa_cib_anon_update(XML_CIB_TAG_NODES, tmp1);
+ tmp1 = pcmk__xe_create(NULL, PCMK_XE_NODE);
+ crm_xml_add(tmp1, PCMK_XA_ID, pcmk__cluster_node_uuid(join_node));
+ crm_xml_add(tmp1, PCMK_XA_UNAME, join_to);
+ fsa_cib_anon_update(PCMK_XE_NODES, tmp1);
free_xml(tmp1);
if (join_node->join == crm_join_nack_quiet) {
@@ -866,8 +878,9 @@ finalize_join_for(gpointer key, gpointer value, gpointer user_data)
return;
}
- join_node = crm_get_peer(0, join_to);
- if (!crm_is_peer_active(join_node)) {
+ join_node = pcmk__get_node(0, join_to, NULL,
+ pcmk__node_search_cluster_member);
+ if (!pcmk__cluster_is_node_active(join_node)) {
/*
* NACK'ing nodes that the membership layer doesn't know about yet
* simply creates more churn
@@ -896,10 +909,10 @@ finalize_join_for(gpointer key, gpointer value, gpointer user_data)
* node hosts each to the ACK message. This keeps new controllers in
* sync with what has already happened.
*/
- if (crm_remote_peer_cache_size() != 0) {
+ if (pcmk__cluster_num_remote_nodes() > 0) {
GHashTableIter iter;
crm_node_t *node = NULL;
- xmlNode *remotes = create_xml_node(acknak, XML_CIB_TAG_NODES);
+ xmlNode *remotes = pcmk__xe_create(acknak, PCMK_XE_NODES);
g_hash_table_iter_init(&iter, crm_remote_peer_cache);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
@@ -909,16 +922,16 @@ finalize_join_for(gpointer key, gpointer value, gpointer user_data)
continue;
}
- remote = create_xml_node(remotes, XML_CIB_TAG_NODE);
+ remote = pcmk__xe_create(remotes, PCMK_XE_NODE);
pcmk__xe_set_props(remote,
- XML_ATTR_ID, node->uname,
- XML_CIB_TAG_STATE, node->state,
- PCMK__XA_CONN_HOST, node->conn_host,
+ PCMK_XA_ID, node->uname,
+ PCMK__XA_NODE_STATE, node->state,
+ PCMK__XA_CONNECTION_HOST, node->conn_host,
NULL);
}
}
}
- send_cluster_message(join_node, crm_msg_crmd, acknak, TRUE);
+ pcmk__cluster_send_message(join_node, crm_msg_crmd, acknak);
free_xml(acknak);
return;
}
diff --git a/daemons/controld/controld_membership.c b/daemons/controld/controld_membership.c
index f25d1e9..1079d6a 100644
--- a/daemons/controld/controld_membership.c
+++ b/daemons/controld/controld_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,7 +13,6 @@
#include <string.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
#include <crm/cluster/internal.h>
@@ -29,7 +28,7 @@ reap_dead_nodes(gpointer key, gpointer value, gpointer user_data)
{
crm_node_t *node = value;
- if (crm_is_peer_active(node) == FALSE) {
+ if (!pcmk__cluster_is_node_active(node)) {
crm_update_peer_join(__func__, node, crm_join_none);
if(node && node->uname) {
@@ -85,7 +84,7 @@ post_cache_update(int instance)
*/
no_op = create_request(CRM_OP_NOOP, NULL, NULL, CRM_SYSTEM_CRMD,
AM_I_DC ? CRM_SYSTEM_DC : CRM_SYSTEM_CRMD, NULL);
- send_cluster_message(NULL, crm_msg_crmd, no_op, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_crmd, no_op);
free_xml(no_op);
}
@@ -132,19 +131,20 @@ create_node_state_update(crm_node_t *node, int flags, xmlNode *parent,
return NULL;
}
- node_state = create_xml_node(parent, XML_CIB_TAG_STATE);
+ node_state = pcmk__xe_create(parent, PCMK__XE_NODE_STATE);
if (pcmk_is_set(node->flags, crm_remote_node)) {
- pcmk__xe_set_bool_attr(node_state, XML_NODE_IS_REMOTE, true);
+ pcmk__xe_set_bool_attr(node_state, PCMK_XA_REMOTE_NODE, true);
}
- if (crm_xml_add(node_state, XML_ATTR_ID, crm_peer_uuid(node)) == NULL) {
+ if (crm_xml_add(node_state, PCMK_XA_ID,
+ pcmk__cluster_node_uuid(node)) == NULL) {
crm_info("Node update for %s cancelled: no ID", node->uname);
free_xml(node_state);
return NULL;
}
- crm_xml_add(node_state, XML_ATTR_UNAME, node->uname);
+ crm_xml_add(node_state, PCMK_XA_UNAME, node->uname);
if ((flags & node_update_cluster) && node->state) {
if (compare_version(controld_globals.dc_version, "3.18.0") >= 0) {
@@ -162,15 +162,15 @@ create_node_state_update(crm_node_t *node, int flags, xmlNode *parent,
if (flags & node_update_peer) {
if (compare_version(controld_globals.dc_version, "3.18.0") >= 0) {
// A value 0 means the peer is offline in CPG.
- crm_xml_add_ll(node_state, PCMK__XA_CRMD, node->when_online);
+ crm_xml_add_ll(node_state, PCMK_XA_CRMD, node->when_online);
} else {
// @COMPAT DCs < 2.1.7 use online/offline rather than timestamp
- value = OFFLINESTATUS;
+ value = PCMK_VALUE_OFFLINE;
if (pcmk_is_set(node->processes, crm_get_cluster_proc())) {
- value = ONLINESTATUS;
+ value = PCMK_VALUE_ONLINE;
}
- crm_xml_add(node_state, PCMK__XA_CRMD, value);
+ crm_xml_add(node_state, PCMK_XA_CRMD, value);
}
}
@@ -184,11 +184,11 @@ create_node_state_update(crm_node_t *node, int flags, xmlNode *parent,
}
if (flags & node_update_expected) {
- crm_xml_add(node_state, PCMK__XA_EXPECTED, node->expected);
+ crm_xml_add(node_state, PCMK_XA_EXPECTED, node->expected);
}
}
- crm_xml_add(node_state, XML_ATTR_ORIGIN, source);
+ crm_xml_add(node_state, PCMK_XA_CRM_DEBUG_ORIGIN, source);
return node_state;
}
@@ -222,26 +222,22 @@ search_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
return;
}
- if (pcmk__xe_is(output, XML_CIB_TAG_NODE)) {
+ if (pcmk__xe_is(output, PCMK_XE_NODE)) {
node_xml = output;
} else {
- node_xml = pcmk__xml_first_child(output);
+ node_xml = pcmk__xe_first_child(output, PCMK_XE_NODE, NULL, NULL);
}
- for (; node_xml != NULL; node_xml = pcmk__xml_next(node_xml)) {
+ for (; node_xml != NULL; node_xml = pcmk__xe_next_same(node_xml)) {
const char *node_uuid = NULL;
const char *node_uname = NULL;
GHashTableIter iter;
crm_node_t *node = NULL;
gboolean known = FALSE;
- if (!pcmk__xe_is(node_xml, XML_CIB_TAG_NODE)) {
- continue;
- }
-
- node_uuid = crm_element_value(node_xml, XML_ATTR_ID);
- node_uname = crm_element_value(node_xml, XML_ATTR_UNAME);
+ node_uuid = crm_element_value(node_xml, PCMK_XA_ID);
+ node_uname = crm_element_value(node_xml, PCMK_XA_UNAME);
if (node_uuid == NULL || node_uname == NULL) {
continue;
@@ -267,20 +263,19 @@ search_conflicting_node_callback(xmlNode * msg, int call_id, int rc,
crm_notice("Deleting unknown node %s/%s which has conflicting uname with %s",
node_uuid, node_uname, new_node_uuid);
- delete_call_id = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_NODES,
+ delete_call_id = cib_conn->cmds->remove(cib_conn, PCMK_XE_NODES,
node_xml, cib_scope_local);
- fsa_register_cib_callback(delete_call_id, strdup(node_uuid),
+ fsa_register_cib_callback(delete_call_id, pcmk__str_copy(node_uuid),
remove_conflicting_node_callback);
- node_state_xml = create_xml_node(NULL, XML_CIB_TAG_STATE);
- crm_xml_add(node_state_xml, XML_ATTR_ID, node_uuid);
- crm_xml_add(node_state_xml, XML_ATTR_UNAME, node_uname);
+ node_state_xml = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE);
+ crm_xml_add(node_state_xml, PCMK_XA_ID, node_uuid);
+ crm_xml_add(node_state_xml, PCMK_XA_UNAME, node_uname);
- delete_call_id = cib_conn->cmds->remove(cib_conn,
- XML_CIB_TAG_STATUS,
+ delete_call_id = cib_conn->cmds->remove(cib_conn, PCMK_XE_STATUS,
node_state_xml,
cib_scope_local);
- fsa_register_cib_callback(delete_call_id, strdup(node_uuid),
+ fsa_register_cib_callback(delete_call_id, pcmk__str_copy(node_uuid),
remove_conflicting_node_callback);
free_xml(node_state_xml);
}
@@ -311,10 +306,12 @@ populate_cib_nodes(enum node_update_flags flags, const char *source)
int call_id = 0;
gboolean from_hashtable = TRUE;
- xmlNode *node_list = create_xml_node(NULL, XML_CIB_TAG_NODES);
+ xmlNode *node_list = pcmk__xe_create(NULL, PCMK_XE_NODES);
#if SUPPORT_COROSYNC
- if (!pcmk_is_set(flags, node_update_quick) && is_corosync_cluster()) {
+ if (!pcmk_is_set(flags, node_update_quick)
+ && (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync)) {
+
from_hashtable = pcmk__corosync_add_nodes(node_list);
}
#endif
@@ -337,22 +334,22 @@ populate_cib_nodes(enum node_update_flags flags, const char *source)
}
/* We need both to be valid */
- new_node = create_xml_node(node_list, XML_CIB_TAG_NODE);
- crm_xml_add(new_node, XML_ATTR_ID, node->uuid);
- crm_xml_add(new_node, XML_ATTR_UNAME, node->uname);
+ new_node = pcmk__xe_create(node_list, PCMK_XE_NODE);
+ crm_xml_add(new_node, PCMK_XA_ID, node->uuid);
+ crm_xml_add(new_node, PCMK_XA_UNAME, node->uname);
/* Search and remove unknown nodes with the conflicting uname from CIB */
pcmk__g_strcat(xpath,
- "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
- "/" XML_CIB_TAG_NODES "/" XML_CIB_TAG_NODE
- "[@" XML_ATTR_UNAME "='", node->uname, "']"
- "[@" XML_ATTR_ID "!='", node->uuid, "']", NULL);
+ "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION
+ "/" PCMK_XE_NODES "/" PCMK_XE_NODE
+ "[@" PCMK_XA_UNAME "='", node->uname, "']"
+ "[@" PCMK_XA_ID "!='", node->uuid, "']", NULL);
call_id = cib_conn->cmds->query(cib_conn,
(const char *) xpath->str,
NULL,
cib_scope_local|cib_xpath);
- fsa_register_cib_callback(call_id, strdup(node->uuid),
+ fsa_register_cib_callback(call_id, pcmk__str_copy(node->uuid),
search_conflicting_node_callback);
}
}
@@ -364,7 +361,7 @@ populate_cib_nodes(enum node_update_flags flags, const char *source)
crm_trace("Populating <nodes> section from %s", from_hashtable ? "hashtable" : "cluster");
- if ((controld_update_cib(XML_CIB_TAG_NODES, node_list, cib_scope_local,
+ if ((controld_update_cib(PCMK_XE_NODES, node_list, cib_scope_local,
node_list_update_callback) == pcmk_rc_ok)
&& (crm_peer_cache != NULL) && AM_I_DC) {
/*
@@ -375,7 +372,7 @@ populate_cib_nodes(enum node_update_flags flags, const char *source)
crm_node_t *node = NULL;
free_xml(node_list);
- node_list = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ node_list = pcmk__xe_create(NULL, PCMK_XE_STATUS);
g_hash_table_iter_init(&iter, crm_peer_cache);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
@@ -389,7 +386,7 @@ populate_cib_nodes(enum node_update_flags flags, const char *source)
}
}
- controld_update_cib(XML_CIB_TAG_STATUS, node_list, cib_scope_local,
+ controld_update_cib(PCMK_XE_STATUS, node_list, cib_scope_local,
crmd_node_update_complete);
}
free_xml(node_list);
@@ -429,12 +426,12 @@ crm_update_quorum(gboolean quorum, gboolean force_update)
|| force_update)) {
xmlNode *update = NULL;
- update = create_xml_node(NULL, XML_TAG_CIB);
- crm_xml_add_int(update, XML_ATTR_HAVE_QUORUM, quorum);
- crm_xml_add(update, XML_ATTR_DC_UUID, controld_globals.our_uuid);
+ update = pcmk__xe_create(NULL, PCMK_XE_CIB);
+ crm_xml_add_int(update, PCMK_XA_HAVE_QUORUM, quorum);
+ crm_xml_add(update, PCMK_XA_DC_UUID, controld_globals.our_uuid);
crm_debug("Updating quorum status to %s", pcmk__btoa(quorum));
- controld_update_cib(XML_TAG_CIB, update, cib_scope_local,
+ controld_update_cib(PCMK_XE_CIB, update, cib_scope_local,
cib_quorum_update_complete);
free_xml(update);
@@ -453,11 +450,11 @@ crm_update_quorum(gboolean quorum, gboolean force_update)
* nodes are joining around the same time, so the one that brings us
* to quorum doesn't cause all the remaining ones to be fenced.
*/
- abort_after_delay(INFINITY, pcmk__graph_restart, "Quorum gained",
- 5000);
+ abort_after_delay(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Quorum gained", 5000);
} else {
- abort_transition(INFINITY, pcmk__graph_restart, "Quorum lost",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Quorum lost", NULL);
}
}
diff --git a/daemons/controld/controld_messages.c b/daemons/controld/controld_messages.c
index 39f3c7a..5f7a78c 100644
--- a/daemons/controld/controld_messages.c
+++ b/daemons/controld/controld_messages.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,6 @@
#include <time.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
#include <crm/cib.h>
@@ -22,8 +21,6 @@
#include <pacemaker-controld.h>
-extern void crm_shutdown(int nsig);
-
static enum crmd_fsa_input handle_message(xmlNode *msg,
enum crmd_fsa_cause cause);
static void handle_response(xmlNode *stored_msg);
@@ -102,7 +99,7 @@ register_fsa_input_adv(enum crmd_fsa_cause cause, enum crmd_fsa_input input,
fsa_input2string(input), fsa_cause2string(cause),
(data? "with" : "without"));
- fsa_data = calloc(1, sizeof(fsa_data_t));
+ fsa_data = pcmk__assert_alloc(1, sizeof(fsa_data_t));
fsa_data->id = last_data_id;
fsa_data->fsa_input = input;
fsa_data->fsa_cause = cause;
@@ -191,11 +188,14 @@ fsa_dump_queue(int log_level)
ha_msg_input_t *
copy_ha_msg_input(ha_msg_input_t * orig)
{
- ha_msg_input_t *copy = calloc(1, sizeof(ha_msg_input_t));
+ xmlNode *wrapper = NULL;
+
+ ha_msg_input_t *copy = pcmk__assert_alloc(1, sizeof(ha_msg_input_t));
+
+ copy->msg = (orig != NULL)? pcmk__xml_copy(NULL, orig->msg) : NULL;
- CRM_ASSERT(copy != NULL);
- copy->msg = (orig && orig->msg)? copy_xml(orig->msg) : NULL;
- copy->xml = get_message_xml(copy->msg, F_CRM_DATA);
+ wrapper = pcmk__xe_first_child(copy->msg, PCMK__XE_CRM_XML, NULL, NULL);
+ copy->xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
return copy;
}
@@ -328,7 +328,7 @@ route_message(enum crmd_fsa_cause cause, xmlNode * input)
gboolean
relay_message(xmlNode * msg, gboolean originated_locally)
{
- enum crm_ais_msg_types dest = crm_msg_ais;
+ enum crm_ais_msg_types dest = crm_msg_none;
bool is_for_dc = false;
bool is_for_dcib = false;
bool is_for_te = false;
@@ -346,12 +346,12 @@ relay_message(xmlNode * msg, gboolean originated_locally)
CRM_CHECK(msg != NULL, return TRUE);
- host_to = crm_element_value(msg, F_CRM_HOST_TO);
- sys_to = crm_element_value(msg, F_CRM_SYS_TO);
- sys_from = crm_element_value(msg, F_CRM_SYS_FROM);
- type = crm_element_value(msg, F_TYPE);
- task = crm_element_value(msg, F_CRM_TASK);
- ref = crm_element_value(msg, XML_ATTR_REFERENCE);
+ host_to = crm_element_value(msg, PCMK__XA_CRM_HOST_TO);
+ sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
+ sys_from = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
+ type = crm_element_value(msg, PCMK__XA_T);
+ task = crm_element_value(msg, PCMK__XA_CRM_TASK);
+ ref = crm_element_value(msg, PCMK_XA_REFERENCE);
broadcast = pcmk__str_empty(host_to);
@@ -367,8 +367,9 @@ relay_message(xmlNode * msg, gboolean originated_locally)
}
// Require message type (set by create_request())
- if (!pcmk__str_eq(type, T_CRM, pcmk__str_casei)) {
- crm_warn("Ignoring invalid message %s with type '%s' (not '" T_CRM "')",
+ if (!pcmk__str_eq(type, PCMK__VALUE_CRMD, pcmk__str_none)) {
+ crm_warn("Ignoring invalid message %s with type '%s' "
+ "(not '" PCMK__VALUE_CRMD "')",
ref, pcmk__s(type, ""));
crm_log_xml_trace(msg, "ignored");
return TRUE;
@@ -376,15 +377,16 @@ relay_message(xmlNode * msg, gboolean originated_locally)
// Require a destination subsystem (also set by create_request())
if (sys_to == NULL) {
- crm_warn("Ignoring invalid message %s with no " F_CRM_SYS_TO, ref);
+ crm_warn("Ignoring invalid message %s with no " PCMK__XA_CRM_SYS_TO,
+ ref);
crm_log_xml_trace(msg, "ignored");
return TRUE;
}
// Get the message type appropriate to the destination subsystem
- if (is_corosync_cluster()) {
- dest = text2msg_type(sys_to);
- if ((dest < crm_msg_ais) || (dest > crm_msg_stonith_ng)) {
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
+ dest = pcmk__cluster_parse_msg_type(sys_to);
+ if (dest == crm_msg_none) {
/* Unrecognized value, use a sane default
*
* @TODO Maybe we should bail instead
@@ -427,10 +429,12 @@ relay_message(xmlNode * msg, gboolean originated_locally)
is_local = true;
} else if (is_for_crm && pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
- xmlNode *msg_data = get_message_xml(msg, F_CRM_DATA);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL,
+ NULL);
+ xmlNode *msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
const char *mode = crm_element_value(msg_data, PCMK__XA_MODE);
- if (pcmk__str_eq(mode, XML_TAG_CIB, pcmk__str_casei)) {
+ if (pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
// Local delete of an offline node's resource history
is_local = true;
}
@@ -458,9 +462,10 @@ relay_message(xmlNode * msg, gboolean originated_locally)
ref, pcmk__s(host_to, "broadcast"));
crm_log_xml_trace(msg, "relayed");
if (!broadcast) {
- node_to = crm_get_peer(0, host_to);
+ node_to = pcmk__get_node(0, host_to, NULL,
+ pcmk__node_search_cluster_member);
}
- send_cluster_message(node_to, dest, msg, TRUE);
+ pcmk__cluster_send_message(node_to, dest, msg);
return TRUE;
}
@@ -484,7 +489,8 @@ relay_message(xmlNode * msg, gboolean originated_locally)
}
if (!broadcast) {
- node_to = pcmk__search_cluster_node_cache(0, host_to, NULL);
+ node_to = pcmk__search_node_caches(0, host_to,
+ pcmk__node_search_cluster_member);
if (node_to == NULL) {
crm_warn("Ignoring message %s because node %s is unknown",
ref, host_to);
@@ -496,7 +502,7 @@ relay_message(xmlNode * msg, gboolean originated_locally)
crm_trace("Relay message %s to %s",
ref, pcmk__s(host_to, "all peers"));
crm_log_xml_trace(msg, "relayed");
- send_cluster_message(node_to, dest, msg, TRUE);
+ pcmk__cluster_send_message(node_to, dest, msg);
return TRUE;
}
@@ -539,10 +545,11 @@ bool
controld_authorize_ipc_message(const xmlNode *client_msg, pcmk__client_t *curr_client,
const char *proxy_session)
{
+ xmlNode *wrapper = NULL;
xmlNode *message_data = NULL;
const char *client_name = NULL;
- const char *op = crm_element_value(client_msg, F_CRM_TASK);
- const char *ref = crm_element_value(client_msg, XML_ATTR_REFERENCE);
+ const char *op = crm_element_value(client_msg, PCMK__XA_CRM_TASK);
+ const char *ref = crm_element_value(client_msg, PCMK_XA_REFERENCE);
const char *uuid = (curr_client? curr_client->id : proxy_session);
if (uuid == NULL) {
@@ -556,27 +563,28 @@ controld_authorize_ipc_message(const xmlNode *client_msg, pcmk__client_t *curr_c
return true;
}
- message_data = get_message_xml(client_msg, F_CRM_DATA);
+ wrapper = pcmk__xe_first_child(client_msg, PCMK__XE_CRM_XML, NULL, NULL);
+ message_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
- client_name = crm_element_value(message_data, "client_name");
+ client_name = crm_element_value(message_data, PCMK__XA_CLIENT_NAME);
if (pcmk__str_empty(client_name)) {
crm_warn("IPC hello from client rejected: No client name",
CRM_XS " ref=%s uuid=%s", (ref? ref : "none"), uuid);
goto rejected;
}
- if (!authorize_version(message_data, "major_version", client_name, ref,
- uuid)) {
+ if (!authorize_version(message_data, PCMK__XA_MAJOR_VERSION, client_name,
+ ref, uuid)) {
goto rejected;
}
- if (!authorize_version(message_data, "minor_version", client_name, ref,
- uuid)) {
+ if (!authorize_version(message_data, PCMK__XA_MINOR_VERSION, client_name,
+ ref, uuid)) {
goto rejected;
}
crm_trace("Validated IPC hello from client %s", client_name);
crm_log_xml_trace(client_msg, "hello");
if (curr_client) {
- curr_client->userdata = strdup(client_name);
+ curr_client->userdata = pcmk__str_copy(client_name);
}
controld_trigger_fsa();
return false;
@@ -596,16 +604,17 @@ handle_message(xmlNode *msg, enum crmd_fsa_cause cause)
CRM_CHECK(msg != NULL, return I_NULL);
- type = crm_element_value(msg, F_CRM_MSG_TYPE);
- if (pcmk__str_eq(type, XML_ATTR_REQUEST, pcmk__str_none)) {
+ type = crm_element_value(msg, PCMK__XA_SUBT);
+ if (pcmk__str_eq(type, PCMK__VALUE_REQUEST, pcmk__str_none)) {
return handle_request(msg, cause);
+ }
- } else if (pcmk__str_eq(type, XML_ATTR_RESPONSE, pcmk__str_none)) {
+ if (pcmk__str_eq(type, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
handle_response(msg);
return I_NULL;
}
- crm_warn("Ignoring message with unknown " F_CRM_MSG_TYPE " '%s'",
+ crm_warn("Ignoring message with unknown " PCMK__XA_SUBT" '%s'",
pcmk__s(type, ""));
crm_log_xml_trace(msg, "bad");
return I_NULL;
@@ -620,31 +629,36 @@ handle_failcount_op(xmlNode * stored_msg)
char *interval_spec = NULL;
guint interval_ms = 0;
gboolean is_remote_node = FALSE;
- xmlNode *xml_op = get_message_xml(stored_msg, F_CRM_DATA);
+
+ xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML, NULL,
+ NULL);
+ xmlNode *xml_op = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
if (xml_op) {
- xmlNode *xml_rsc = first_named_child(xml_op, XML_CIB_TAG_RESOURCE);
- xmlNode *xml_attrs = first_named_child(xml_op, XML_TAG_ATTRS);
+ xmlNode *xml_rsc = pcmk__xe_first_child(xml_op, PCMK_XE_PRIMITIVE, NULL,
+ NULL);
+ xmlNode *xml_attrs = pcmk__xe_first_child(xml_op, PCMK__XE_ATTRIBUTES,
+ NULL, NULL);
if (xml_rsc) {
- rsc = ID(xml_rsc);
+ rsc = pcmk__xe_id(xml_rsc);
}
if (xml_attrs) {
op = crm_element_value(xml_attrs,
- CRM_META "_" XML_RSC_ATTR_CLEAR_OP);
+ CRM_META "_" PCMK__META_CLEAR_FAILURE_OP);
crm_element_value_ms(xml_attrs,
- CRM_META "_" XML_RSC_ATTR_CLEAR_INTERVAL,
+ CRM_META "_" PCMK__META_CLEAR_FAILURE_INTERVAL,
&interval_ms);
}
}
- uname = crm_element_value(xml_op, XML_LRM_ATTR_TARGET);
+ uname = crm_element_value(xml_op, PCMK__META_ON_NODE);
if ((rsc == NULL) || (uname == NULL)) {
crm_log_xml_warn(stored_msg, "invalid failcount op");
return I_NULL;
}
- if (crm_element_value(xml_op, XML_LRM_ATTR_ROUTER_NODE)) {
+ if (crm_element_value(xml_op, PCMK__XA_ROUTER_NODE)) {
is_remote_node = TRUE;
}
@@ -669,7 +683,9 @@ static enum crmd_fsa_input
handle_lrm_delete(xmlNode *stored_msg)
{
const char *mode = NULL;
- xmlNode *msg_data = get_message_xml(stored_msg, F_CRM_DATA);
+ xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML, NULL,
+ NULL);
+ xmlNode *msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
CRM_CHECK(msg_data != NULL, return I_NULL);
@@ -679,14 +695,14 @@ handle_lrm_delete(xmlNode *stored_msg)
* CIB, and do some bookkeeping in the controller.
*
* However, if the affected node is offline, the client will specify
- * mode="cib" which means the controller receiving the operation should
- * clear the resource's history from the CIB and nothing else. This is used
- * to clear shutdown locks.
+ * mode=PCMK__VALUE_CIB which means the controller receiving the operation
+ * should clear the resource's history from the CIB and nothing else. This
+ * is used to clear shutdown locks.
*/
mode = crm_element_value(msg_data, PCMK__XA_MODE);
- if ((mode == NULL) || strcmp(mode, XML_TAG_CIB)) {
+ if (!pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
// Relay to affected node
- crm_xml_add(stored_msg, F_CRM_SYS_TO, CRM_SYSTEM_LRMD);
+ crm_xml_add(stored_msg, PCMK__XA_CRM_SYS_TO, CRM_SYSTEM_LRMD);
return I_ROUTER;
} else {
@@ -698,13 +714,13 @@ handle_lrm_delete(xmlNode *stored_msg)
xmlNode *rsc_xml = NULL;
int rc = pcmk_rc_ok;
- rsc_xml = first_named_child(msg_data, XML_CIB_TAG_RESOURCE);
+ rsc_xml = pcmk__xe_first_child(msg_data, PCMK_XE_PRIMITIVE, NULL, NULL);
CRM_CHECK(rsc_xml != NULL, return I_NULL);
- rsc_id = ID(rsc_xml);
- from_sys = crm_element_value(stored_msg, F_CRM_SYS_FROM);
- node = crm_element_value(msg_data, XML_LRM_ATTR_TARGET);
- user_name = pcmk__update_acl_user(stored_msg, F_CRM_USER, NULL);
+ rsc_id = pcmk__xe_id(rsc_xml);
+ from_sys = crm_element_value(stored_msg, PCMK__XA_CRM_SYS_FROM);
+ node = crm_element_value(msg_data, PCMK__META_ON_NODE);
+ user_name = pcmk__update_acl_user(stored_msg, PCMK__XA_CRM_USER, NULL);
crm_debug("Handling " CRM_OP_LRM_DELETE " for %s on %s locally%s%s "
"(clearing CIB resource history only)", rsc_id, node,
(user_name? " for user " : ""), (user_name? user_name : ""));
@@ -715,19 +731,20 @@ handle_lrm_delete(xmlNode *stored_msg)
crmd_cib_smart_opt());
}
- //Notify client and tengine.(Only notify tengine if mode = "cib" and CRM_OP_LRM_DELETE.)
+ /* Notify client. Also notify tengine if mode=PCMK__VALUE_CIB and
+ * op=CRM_OP_LRM_DELETE.
+ */
if (from_sys) {
lrmd_event_data_t *op = NULL;
- const char *from_host = crm_element_value(stored_msg,
- F_CRM_HOST_FROM);
+ const char *from_host = crm_element_value(stored_msg, PCMK__XA_SRC);
const char *transition;
if (strcmp(from_sys, CRM_SYSTEM_TENGINE)) {
transition = crm_element_value(msg_data,
- XML_ATTR_TRANSITION_KEY);
+ PCMK__XA_TRANSITION_KEY);
} else {
transition = crm_element_value(stored_msg,
- XML_ATTR_TRANSITION_KEY);
+ PCMK__XA_TRANSITION_KEY);
}
crm_info("Notifying %s on %s that %s was%s deleted",
@@ -735,10 +752,10 @@ handle_lrm_delete(xmlNode *stored_msg)
((rc == pcmk_rc_ok)? "" : " not"));
op = lrmd_new_event(rsc_id, PCMK_ACTION_DELETE, 0);
op->type = lrmd_event_exec_complete;
- op->user_data = strdup(transition? transition : FAKE_TE_ID);
+ op->user_data = pcmk__str_copy(pcmk__s(transition, FAKE_TE_ID));
op->params = pcmk__strkey_table(free, free);
- g_hash_table_insert(op->params, strdup(XML_ATTR_CRM_VERSION),
- strdup(CRM_FEATURE_SET));
+ pcmk__insert_dup(op->params, PCMK_XA_CRM_FEATURE_SET,
+ CRM_FEATURE_SET);
controld_rc2event(op, rc);
controld_ack_event_directly(from_host, from_sys, NULL, op, rsc_id);
lrmd_free_event(op);
@@ -759,7 +776,7 @@ static enum crmd_fsa_input
handle_remote_state(const xmlNode *msg)
{
const char *conn_host = NULL;
- const char *remote_uname = ID(msg);
+ const char *remote_uname = pcmk__xe_id(msg);
crm_node_t *remote_peer;
bool remote_is_up = false;
int rc = pcmk_rc_ok;
@@ -768,14 +785,14 @@ handle_remote_state(const xmlNode *msg)
CRM_CHECK(remote_uname && rc == pcmk_rc_ok, return I_NULL);
- remote_peer = crm_remote_peer_get(remote_uname);
+ remote_peer = pcmk__cluster_lookup_remote_node(remote_uname);
CRM_CHECK(remote_peer, return I_NULL);
pcmk__update_peer_state(__func__, remote_peer,
remote_is_up ? CRM_NODE_MEMBER : CRM_NODE_LOST,
0);
- conn_host = crm_element_value(msg, PCMK__XA_CONN_HOST);
+ conn_host = crm_element_value(msg, PCMK__XA_CONNECTION_HOST);
if (conn_host) {
pcmk__str_update(&remote_peer->conn_host, conn_host);
} else if (remote_peer->conn_host) {
@@ -802,18 +819,18 @@ handle_ping(const xmlNode *msg)
// Build reply
- ping = create_xml_node(NULL, XML_CRM_TAG_PING);
- value = crm_element_value(msg, F_CRM_SYS_TO);
- crm_xml_add(ping, XML_PING_ATTR_SYSFROM, value);
+ ping = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
+ value = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
+ crm_xml_add(ping, PCMK__XA_CRM_SUBSYSTEM, value);
// Add controller state
value = fsa_state2string(controld_globals.fsa_state);
- crm_xml_add(ping, XML_PING_ATTR_CRMDSTATE, value);
+ crm_xml_add(ping, PCMK__XA_CRMD_STATE, value);
crm_notice("Current ping state: %s", value); // CTS needs this
// Add controller health
// @TODO maybe do some checks to determine meaningful status
- crm_xml_add(ping, XML_PING_ATTR_STATUS, "ok");
+ crm_xml_add(ping, PCMK_XA_RESULT, "ok");
// Send reply
reply = create_reply(msg, ping);
@@ -843,13 +860,13 @@ handle_node_list(const xmlNode *request)
xmlNode *reply_data = NULL;
// Create message data for reply
- reply_data = create_xml_node(NULL, XML_CIB_TAG_NODES);
+ reply_data = pcmk__xe_create(NULL, PCMK_XE_NODES);
g_hash_table_iter_init(&iter, crm_peer_cache);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
- xmlNode *xml = create_xml_node(reply_data, XML_CIB_TAG_NODE);
+ xmlNode *xml = pcmk__xe_create(reply_data, PCMK_XE_NODE);
- crm_xml_add_ll(xml, XML_ATTR_ID, (long long) node->id); // uint32_t
- crm_xml_add(xml, XML_ATTR_UNAME, node->uname);
+ crm_xml_add_ll(xml, PCMK_XA_ID, (long long) node->id); // uint32_t
+ crm_xml_add(xml, PCMK_XA_UNAME, node->uname);
crm_xml_add(xml, PCMK__XA_IN_CCM, node->state);
}
@@ -883,32 +900,32 @@ handle_node_info_request(const xmlNode *msg)
// Build reply
- reply_data = create_xml_node(NULL, XML_CIB_TAG_NODE);
- crm_xml_add(reply_data, XML_PING_ATTR_SYSFROM, CRM_SYSTEM_CRMD);
+ reply_data = pcmk__xe_create(NULL, PCMK_XE_NODE);
+ crm_xml_add(reply_data, PCMK__XA_CRM_SUBSYSTEM, CRM_SYSTEM_CRMD);
// Add whether current partition has quorum
- pcmk__xe_set_bool_attr(reply_data, XML_ATTR_HAVE_QUORUM,
+ pcmk__xe_set_bool_attr(reply_data, PCMK_XA_HAVE_QUORUM,
pcmk_is_set(controld_globals.flags,
controld_has_quorum));
// Check whether client requested node info by ID and/or name
- crm_element_value_int(msg, XML_ATTR_ID, &node_id);
+ crm_element_value_int(msg, PCMK_XA_ID, &node_id);
if (node_id < 0) {
node_id = 0;
}
- value = crm_element_value(msg, XML_ATTR_UNAME);
+ value = crm_element_value(msg, PCMK_XA_UNAME);
// Default to local node if none given
if ((node_id == 0) && (value == NULL)) {
value = controld_globals.our_nodename;
}
- node = pcmk__search_node_caches(node_id, value, CRM_GET_PEER_ANY);
+ node = pcmk__search_node_caches(node_id, value, pcmk__node_search_any);
if (node) {
- crm_xml_add(reply_data, XML_ATTR_ID, node->uuid);
- crm_xml_add(reply_data, XML_ATTR_UNAME, node->uname);
- crm_xml_add(reply_data, PCMK__XA_CRMD, node->state);
- pcmk__xe_set_bool_attr(reply_data, XML_NODE_IS_REMOTE,
+ crm_xml_add(reply_data, PCMK_XA_ID, node->uuid);
+ crm_xml_add(reply_data, PCMK_XA_UNAME, node->uname);
+ crm_xml_add(reply_data, PCMK_XA_CRMD, node->state);
+ pcmk__xe_set_bool_attr(reply_data, PCMK_XA_REMOTE_NODE,
pcmk_is_set(node->flags, crm_remote_node));
}
@@ -927,7 +944,7 @@ handle_node_info_request(const xmlNode *msg)
static void
verify_feature_set(xmlNode *msg)
{
- const char *dc_version = crm_element_value(msg, XML_ATTR_CRM_VERSION);
+ const char *dc_version = crm_element_value(msg, PCMK_XA_CRM_FEATURE_SET);
if (dc_version == NULL) {
/* All we really know is that the DC feature set is older than 3.1.0,
@@ -953,7 +970,7 @@ verify_feature_set(xmlNode *msg)
static enum crmd_fsa_input
handle_shutdown_self_ack(xmlNode *stored_msg)
{
- const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM);
+ const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
if (pcmk_is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
// The expected case -- we initiated own shutdown sequence
@@ -986,7 +1003,7 @@ handle_shutdown_self_ack(xmlNode *stored_msg)
static enum crmd_fsa_input
handle_shutdown_ack(xmlNode *stored_msg)
{
- const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM);
+ const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
if (host_from == NULL) {
crm_warn("Ignoring shutdown request without origin specified");
@@ -1016,19 +1033,20 @@ static enum crmd_fsa_input
handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
{
xmlNode *msg = NULL;
- const char *op = crm_element_value(stored_msg, F_CRM_TASK);
+ const char *op = crm_element_value(stored_msg, PCMK__XA_CRM_TASK);
/* Optimize this for the DC - it has the most to do */
crm_log_xml_trace(stored_msg, "request");
if (op == NULL) {
- crm_warn("Ignoring request without " F_CRM_TASK);
+ crm_warn("Ignoring request without " PCMK__XA_CRM_TASK);
return I_NULL;
}
if (strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0) {
- const char *from = crm_element_value(stored_msg, F_CRM_HOST_FROM);
- crm_node_t *node = pcmk__search_cluster_node_cache(0, from, NULL);
+ const char *from = crm_element_value(stored_msg, PCMK__XA_SRC);
+ crm_node_t *node =
+ pcmk__search_node_caches(0, from, pcmk__node_search_cluster_member);
pcmk__update_peer_expected(__func__, node, CRMD_JOINSTATE_DOWN);
if(AM_I_DC == FALSE) {
@@ -1099,11 +1117,13 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
} else if (strcmp(op, CRM_OP_JOIN_OFFER) == 0) {
verify_feature_set(stored_msg);
- crm_debug("Raising I_JOIN_OFFER: join-%s", crm_element_value(stored_msg, F_CRM_JOIN_ID));
+ crm_debug("Raising I_JOIN_OFFER: join-%s",
+ crm_element_value(stored_msg, PCMK__XA_JOIN_ID));
return I_JOIN_OFFER;
} else if (strcmp(op, CRM_OP_JOIN_ACKNAK) == 0) {
- crm_debug("Raising I_JOIN_RESULT: join-%s", crm_element_value(stored_msg, F_CRM_JOIN_ID));
+ crm_debug("Raising I_JOIN_RESULT: join-%s",
+ crm_element_value(stored_msg, PCMK__XA_JOIN_ID));
return I_JOIN_RESULT;
} else if (strcmp(op, CRM_OP_LRM_DELETE) == 0) {
@@ -1113,18 +1133,12 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
|| (strcmp(op, CRM_OP_LRM_REFRESH) == 0) // @COMPAT
|| (strcmp(op, CRM_OP_REPROBE) == 0)) {
- crm_xml_add(stored_msg, F_CRM_SYS_TO, CRM_SYSTEM_LRMD);
+ crm_xml_add(stored_msg, PCMK__XA_CRM_SYS_TO, CRM_SYSTEM_LRMD);
return I_ROUTER;
} else if (strcmp(op, CRM_OP_NOOP) == 0) {
return I_NULL;
- } else if (strcmp(op, CRM_OP_LOCAL_SHUTDOWN) == 0) {
-
- crm_shutdown(SIGTERM);
- /*return I_SHUTDOWN; */
- return I_NULL;
-
} else if (strcmp(op, CRM_OP_PING) == 0) {
return handle_ping(stored_msg);
@@ -1135,12 +1149,12 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
int id = 0;
const char *name = NULL;
- crm_element_value_int(stored_msg, XML_ATTR_ID, &id);
- name = crm_element_value(stored_msg, XML_ATTR_UNAME);
+ crm_element_value_int(stored_msg, PCMK_XA_ID, &id);
+ name = crm_element_value(stored_msg, PCMK_XA_UNAME);
if(cause == C_IPC_MESSAGE) {
msg = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
- if (send_cluster_message(NULL, crm_msg_crmd, msg, TRUE) == FALSE) {
+ if (!pcmk__cluster_send_message(NULL, crm_msg_crmd, msg)) {
crm_err("Could not instruct peers to remove references to node %s/%u", name, id);
} else {
crm_notice("Instructing peers to remove references to node %s/%u", name, id);
@@ -1148,7 +1162,7 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
free_xml(msg);
} else {
- reap_crm_member(id, name);
+ pcmk__cluster_forget_cluster_node(id, name);
/* If we're forgetting this node, also forget any failures to fence
* it, so we don't carry that over to any node added later with the
@@ -1158,7 +1172,9 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
}
} else if (strcmp(op, CRM_OP_MAINTENANCE_NODES) == 0) {
- xmlNode *xml = get_message_xml(stored_msg, F_CRM_DATA);
+ xmlNode *wrapper = pcmk__xe_first_child(stored_msg, PCMK__XE_CRM_XML,
+ NULL, NULL);
+ xmlNode *xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
remote_ra_process_maintenance_nodes(xml);
@@ -1183,15 +1199,15 @@ handle_request(xmlNode *stored_msg, enum crmd_fsa_cause cause)
static void
handle_response(xmlNode *stored_msg)
{
- const char *op = crm_element_value(stored_msg, F_CRM_TASK);
+ const char *op = crm_element_value(stored_msg, PCMK__XA_CRM_TASK);
crm_log_xml_trace(stored_msg, "reply");
if (op == NULL) {
- crm_warn("Ignoring reply without " F_CRM_TASK);
+ crm_warn("Ignoring reply without " PCMK__XA_CRM_TASK);
} else if (AM_I_DC && strcmp(op, CRM_OP_PECALC) == 0) {
// Check whether scheduler answer been superseded by subsequent request
- const char *msg_ref = crm_element_value(stored_msg, XML_ATTR_REFERENCE);
+ const char *msg_ref = crm_element_value(stored_msg, PCMK_XA_REFERENCE);
if (msg_ref == NULL) {
crm_err("%s - Ignoring calculation with no reference", op);
@@ -1212,7 +1228,7 @@ handle_response(xmlNode *stored_msg)
|| strcmp(op, CRM_OP_SHUTDOWN_REQ) == 0 || strcmp(op, CRM_OP_SHUTDOWN) == 0) {
} else {
- const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM);
+ const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
crm_err("Unexpected response (op=%s, src=%s) sent to the %s",
op, host_from, AM_I_DC ? "DC" : "controller");
@@ -1230,7 +1246,7 @@ handle_shutdown_request(xmlNode * stored_msg)
*/
char *now_s = NULL;
- const char *host_from = crm_element_value(stored_msg, F_CRM_HOST_FROM);
+ const char *host_from = crm_element_value(stored_msg, PCMK__XA_SRC);
if (host_from == NULL) {
/* we're shutting down and the DC */
@@ -1242,7 +1258,7 @@ handle_shutdown_request(xmlNode * stored_msg)
crm_log_xml_trace(stored_msg, "message");
now_s = pcmk__ttoa(time(NULL));
- update_attrd(host_from, XML_CIB_ATTR_SHUTDOWN, now_s, NULL, FALSE);
+ update_attrd(host_from, PCMK__NODE_ATTR_SHUTDOWN, now_s, NULL, FALSE);
free(now_s);
/* will be picked up by the TE as long as its running */
@@ -1258,8 +1274,8 @@ send_msg_via_ipc(xmlNode * msg, const char *sys)
client_channel = pcmk__find_client_by_id(sys);
- if (crm_element_value(msg, F_CRM_HOST_FROM) == NULL) {
- crm_xml_add(msg, F_CRM_HOST_FROM, controld_globals.our_nodename);
+ if (crm_element_value(msg, PCMK__XA_SRC) == NULL) {
+ crm_xml_add(msg, PCMK__XA_SRC, controld_globals.our_nodename);
}
if (client_channel != NULL) {
@@ -1267,16 +1283,21 @@ send_msg_via_ipc(xmlNode * msg, const char *sys)
pcmk__ipc_send_xml(client_channel, 0, msg, crm_ipc_server_event);
} else if (pcmk__str_eq(sys, CRM_SYSTEM_TENGINE, pcmk__str_none)) {
- xmlNode *data = get_message_xml(msg, F_CRM_DATA);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL,
+ NULL);
+ xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
process_te_message(msg, data);
} else if (pcmk__str_eq(sys, CRM_SYSTEM_LRMD, pcmk__str_none)) {
fsa_data_t fsa_data;
ha_msg_input_t fsa_input;
+ xmlNode *wrapper = NULL;
fsa_input.msg = msg;
- fsa_input.xml = get_message_xml(msg, F_CRM_DATA);
+
+ wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL, NULL);
+ fsa_input.xml = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
fsa_data.id = 0;
fsa_data.actions = 0;
@@ -1323,14 +1344,15 @@ broadcast_remote_state_message(const char *node_name, bool node_up)
crm_info("Notifying cluster of Pacemaker Remote node %s %s",
node_name, node_up? "coming up" : "going down");
- crm_xml_add(msg, XML_ATTR_ID, node_name);
+ crm_xml_add(msg, PCMK_XA_ID, node_name);
pcmk__xe_set_bool_attr(msg, PCMK__XA_IN_CCM, node_up);
if (node_up) {
- crm_xml_add(msg, PCMK__XA_CONN_HOST, controld_globals.our_nodename);
+ crm_xml_add(msg, PCMK__XA_CONNECTION_HOST,
+ controld_globals.our_nodename);
}
- send_cluster_message(NULL, crm_msg_crmd, msg, TRUE);
+ pcmk__cluster_send_message(NULL, crm_msg_crmd, msg);
free_xml(msg);
}
diff --git a/daemons/controld/controld_metadata.c b/daemons/controld/controld_metadata.c
index c813ceb..37df38c 100644
--- a/daemons/controld/controld_metadata.c
+++ b/daemons/controld/controld_metadata.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 the Pacemaker project contributors
+ * Copyright 2017-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -70,25 +70,18 @@ metadata_cache_reset(GHashTable *mdc)
static struct ra_param_s *
ra_param_from_xml(xmlNode *param_xml)
{
- const char *param_name = crm_element_value(param_xml, "name");
+ const char *param_name = crm_element_value(param_xml, PCMK_XA_NAME);
struct ra_param_s *p;
- p = calloc(1, sizeof(struct ra_param_s));
- if (p == NULL) {
- return NULL;
- }
+ p = pcmk__assert_alloc(1, sizeof(struct ra_param_s));
- p->rap_name = strdup(param_name);
- if (p->rap_name == NULL) {
- free(p);
- return NULL;
- }
+ p->rap_name = pcmk__str_copy(param_name);
- if (pcmk__xe_attr_is_true(param_xml, "reloadable")) {
+ if (pcmk__xe_attr_is_true(param_xml, PCMK_XA_RELOADABLE)) {
controld_set_ra_param_flags(p, ra_param_reloadable);
}
- if (pcmk__xe_attr_is_true(param_xml, "unique")) {
+ if (pcmk__xe_attr_is_true(param_xml, PCMK_XA_UNIQUE)) {
controld_set_ra_param_flags(p, ra_param_unique);
}
@@ -139,21 +132,19 @@ controld_cache_metadata(GHashTable *mdc, const lrmd_rsc_info_t *rsc,
goto err;
}
- metadata = string2xml(metadata_str);
+ metadata = pcmk__xml_parse(metadata_str);
if (!metadata) {
reason = "Metadata is not valid XML";
goto err;
}
- md = calloc(1, sizeof(struct ra_metadata_s));
- if (md == NULL) {
- reason = "Could not allocate memory";
- goto err;
- }
+ md = pcmk__assert_alloc(1, sizeof(struct ra_metadata_s));
if (strcmp(rsc->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
xmlChar *content = NULL;
- xmlNode *version_element = first_named_child(metadata, "version");
+ xmlNode *version_element = pcmk__xe_first_child(metadata,
+ PCMK_XE_VERSION, NULL,
+ NULL);
if (version_element != NULL) {
content = xmlNodeGetContent(version_element);
@@ -166,11 +157,11 @@ controld_cache_metadata(GHashTable *mdc, const lrmd_rsc_info_t *rsc,
}
// Check supported actions
- match = first_named_child(metadata, "actions");
- for (match = first_named_child(match, "action"); match != NULL;
- match = crm_next_same_xml(match)) {
+ match = pcmk__xe_first_child(metadata, PCMK_XE_ACTIONS, NULL, NULL);
+ for (match = pcmk__xe_first_child(match, PCMK_XE_ACTION, NULL, NULL);
+ match != NULL; match = pcmk__xe_next_same(match)) {
- const char *action_name = crm_element_value(match, "name");
+ const char *action_name = crm_element_value(match, PCMK_XA_NAME);
if (pcmk__str_eq(action_name, PCMK_ACTION_RELOAD_AGENT,
pcmk__str_none)) {
@@ -188,15 +179,15 @@ controld_cache_metadata(GHashTable *mdc, const lrmd_rsc_info_t *rsc,
}
// Build a parameter list
- match = first_named_child(metadata, "parameters");
- for (match = first_named_child(match, "parameter"); match != NULL;
- match = crm_next_same_xml(match)) {
+ match = pcmk__xe_first_child(metadata, PCMK_XE_PARAMETERS, NULL, NULL);
+ for (match = pcmk__xe_first_child(match, PCMK_XE_PARAMETER, NULL, NULL);
+ match != NULL; match = pcmk__xe_next_same(match)) {
- const char *param_name = crm_element_value(match, "name");
+ const char *param_name = crm_element_value(match, PCMK_XA_NAME);
if (param_name == NULL) {
- crm_warn("Metadata for %s:%s:%s has parameter without a name",
- rsc->standard, rsc->provider, rsc->type);
+ crm_warn("Metadata for %s:%s:%s has parameter without a "
+ PCMK_XA_NAME, rsc->standard, rsc->provider, rsc->type);
} else {
struct ra_param_s *p = ra_param_from_xml(match);
diff --git a/daemons/controld/controld_remote_ra.c b/daemons/controld/controld_remote_ra.c
index d692ef6..4bbf80c 100644
--- a/daemons/controld/controld_remote_ra.c
+++ b/daemons/controld/controld_remote_ra.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.
*
@@ -10,7 +10,7 @@
#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 <crm/lrmd.h>
#include <crm/lrmd_internal.h>
@@ -206,7 +206,8 @@ should_purge_attributes(crm_node_t *node)
/* Get the node that was hosting the remote connection resource from the
* peer cache. That's the one we really care about here.
*/
- conn_node = crm_get_peer(0, node->conn_host);
+ conn_node = pcmk__get_node(0, node->conn_host, NULL,
+ pcmk__node_search_cluster_member);
if (conn_node == NULL) {
return purge;
}
@@ -296,7 +297,7 @@ remote_node_up(const char *node_name)
update_attrd(node_name, CRM_OP_PROBED, NULL, NULL, TRUE);
/* Ensure node is in the remote peer cache with member status */
- node = crm_remote_peer_get(node_name);
+ node = pcmk__cluster_lookup_remote_node(node_name);
CRM_CHECK(node != NULL, return);
purge_remote_node_attrs(call_opt, node);
@@ -324,24 +325,24 @@ remote_node_up(const char *node_name)
*/
broadcast_remote_state_message(node_name, true);
- update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ update = pcmk__xe_create(NULL, PCMK_XE_STATUS);
state = create_node_state_update(node, node_update_cluster, update,
__func__);
- /* Clear the XML_NODE_IS_FENCED flag in the node state. If the node ever
+ /* Clear the PCMK__XA_NODE_FENCED flag in the node state. If the node ever
* needs to be fenced, this flag will allow various actions to determine
* whether the fencing has happened yet.
*/
- crm_xml_add(state, XML_NODE_IS_FENCED, "0");
+ crm_xml_add(state, PCMK__XA_NODE_FENCED, "0");
/* TODO: If the remote connection drops, and this (async) CIB update either
* failed or has not yet completed, later actions could mistakenly think the
- * node has already been fenced (if the XML_NODE_IS_FENCED attribute was
+ * node has already been fenced (if the PCMK__XA_NODE_FENCED attribute was
* previously set, because it won't have been cleared). This could prevent
* actual fencing or allow recurring monitor failures to be cleared too
* soon. Ideally, we wouldn't rely on the CIB for the fenced status.
*/
- controld_update_cib(XML_CIB_TAG_STATUS, update, call_opt, NULL);
+ controld_update_cib(PCMK_XE_STATUS, update, call_opt, NULL);
free_xml(update);
}
@@ -379,7 +380,7 @@ remote_node_down(const char *node_name, const enum down_opts opts)
}
/* Ensure node is in the remote peer cache with lost state */
- node = crm_remote_peer_get(node_name);
+ node = pcmk__cluster_lookup_remote_node(node_name);
CRM_CHECK(node != NULL, return);
pcmk__update_peer_state(__func__, node, CRM_NODE_LOST, 0);
@@ -387,9 +388,9 @@ remote_node_down(const char *node_name, const enum down_opts opts)
broadcast_remote_state_message(node_name, false);
/* Update CIB node state */
- update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ update = pcmk__xe_create(NULL, PCMK_XE_STATUS);
create_node_state_update(node, node_update_cluster, update, __func__);
- controld_update_cib(XML_CIB_TAG_STATUS, update, call_opt, NULL);
+ controld_update_cib(PCMK_XE_STATUS, update, call_opt, NULL);
free_xml(update);
}
@@ -419,7 +420,7 @@ check_remote_node_state(const remote_ra_cmd_t *cmd)
* it hasn't been tracking the remote node, and other code relies on
* the cache to distinguish remote nodes from unseen cluster nodes.
*/
- crm_node_t *node = crm_remote_peer_get(cmd->rsc_id);
+ crm_node_t *node = pcmk__cluster_lookup_remote_node(cmd->rsc_id);
CRM_CHECK(node != NULL, return);
pcmk__update_peer_state(__func__, node, CRM_NODE_MEMBER, 0);
@@ -437,7 +438,7 @@ check_remote_node_state(const remote_ra_cmd_t *cmd)
* so if the connection migrated elsewhere and we aren't DC,
* un-cache the node, so we don't have stale info
*/
- crm_remote_peer_cache_remove(cmd->rsc_id);
+ pcmk__cluster_forget_remote_node(cmd->rsc_id);
}
}
}
@@ -493,7 +494,7 @@ report_remote_ra_result(remote_ra_cmd_t * cmd)
op.params = pcmk__strkey_table(free, free);
for (tmp = cmd->params; tmp; tmp = tmp->next) {
- g_hash_table_insert(op.params, strdup(tmp->key), strdup(tmp->value));
+ pcmk__insert_dup(op.params, tmp->key, tmp->value);
}
}
@@ -861,12 +862,17 @@ handle_remote_ra_start(lrm_state_t * lrm_state, remote_ra_cmd_t * cmd, int timeo
int rc = pcmk_rc_ok;
for (tmp = cmd->params; tmp; tmp = tmp->next) {
- if (pcmk__strcase_any_of(tmp->key, XML_RSC_ATTR_REMOTE_RA_ADDR,
- XML_RSC_ATTR_REMOTE_RA_SERVER, NULL)) {
+ if (pcmk__strcase_any_of(tmp->key,
+ PCMK_REMOTE_RA_ADDR, PCMK_REMOTE_RA_SERVER,
+ NULL)) {
server = tmp->value;
- } else if (pcmk__str_eq(tmp->key, XML_RSC_ATTR_REMOTE_RA_PORT, pcmk__str_casei)) {
+
+ } else if (pcmk__str_eq(tmp->key, PCMK_REMOTE_RA_PORT,
+ pcmk__str_none)) {
port = atoi(tmp->value);
- } else if (pcmk__str_eq(tmp->key, CRM_META "_" XML_RSC_ATTR_CONTAINER, pcmk__str_casei)) {
+
+ } else if (pcmk__str_eq(tmp->key, CRM_META "_" PCMK__META_CONTAINER,
+ pcmk__str_none)) {
lrm_remote_set_flags(lrm_state, controlling_guest);
}
}
@@ -967,9 +973,9 @@ handle_remote_ra_exec(gpointer user_data)
} else if (pcmk__str_any_of(cmd->action, PCMK_ACTION_RELOAD,
PCMK_ACTION_RELOAD_AGENT, NULL)) {
- /* Currently the only reloadable parameter is reconnect_interval,
- * which is only used by the scheduler via the CIB, so reloads are a
- * no-op.
+ /* Currently the only reloadable parameter is
+ * PCMK_REMOTE_RA_RECONNECT_INTERVAL, which is only used by the
+ * scheduler via the CIB, so reloads are a no-op.
*
* @COMPAT DC <2.1.0: We only need to check for "reload" in case
* we're in a rolling upgrade with a DC scheduling "reload" instead
@@ -995,7 +1001,7 @@ remote_ra_data_init(lrm_state_t * lrm_state)
return;
}
- ra_data = calloc(1, sizeof(remote_ra_data_t));
+ ra_data = pcmk__assert_alloc(1, sizeof(remote_ra_data_t));
ra_data->work = mainloop_add_trigger(G_PRIORITY_HIGH, handle_remote_ra_exec, lrm_state);
lrm_state->remote_ra_data = ra_data;
}
@@ -1041,12 +1047,12 @@ remote_ra_get_rsc_info(lrm_state_t * lrm_state, const char *rsc_id)
lrmd_rsc_info_t *info = NULL;
if ((lrm_state_find(rsc_id))) {
- info = calloc(1, sizeof(lrmd_rsc_info_t));
+ info = pcmk__assert_alloc(1, sizeof(lrmd_rsc_info_t));
- info->id = strdup(rsc_id);
- info->type = strdup(REMOTE_LRMD_RA);
- info->standard = strdup(PCMK_RESOURCE_CLASS_OCF);
- info->provider = strdup("pacemaker");
+ info->id = pcmk__str_copy(rsc_id);
+ info->type = pcmk__str_copy(REMOTE_LRMD_RA);
+ info->standard = pcmk__str_copy(PCMK_RESOURCE_CLASS_OCF);
+ info->provider = pcmk__str_copy("pacemaker");
}
return info;
@@ -1202,7 +1208,7 @@ handle_dup:
/* update the userdata */
if (userdata) {
free(cmd->userdata);
- cmd->userdata = strdup(userdata);
+ cmd->userdata = pcmk__str_copy(userdata);
}
/* if we've already reported success, generate a new call id */
@@ -1280,23 +1286,12 @@ controld_execute_remote_agent(const lrm_state_t *lrm_state, const char *rsc_id,
return pcmk_rc_ok;
}
- cmd = calloc(1, sizeof(remote_ra_cmd_t));
- if (cmd == NULL) {
- lrmd_key_value_freeall(params);
- return ENOMEM;
- }
-
- cmd->owner = strdup(lrm_state->node_name);
- cmd->rsc_id = strdup(rsc_id);
- cmd->action = strdup(action);
- cmd->userdata = strdup(userdata);
- if ((cmd->owner == NULL) || (cmd->rsc_id == NULL) || (cmd->action == NULL)
- || (cmd->userdata == NULL)) {
- free_cmd(cmd);
- lrmd_key_value_freeall(params);
- return ENOMEM;
- }
+ cmd = pcmk__assert_alloc(1, sizeof(remote_ra_cmd_t));
+ cmd->owner = pcmk__str_copy(lrm_state->node_name);
+ cmd->rsc_id = pcmk__str_copy(rsc_id);
+ cmd->action = pcmk__str_copy(action);
+ cmd->userdata = pcmk__str_copy(userdata);
cmd->interval_ms = interval_ms;
cmd->timeout = timeout_ms;
cmd->start_delay = start_delay_ms;
@@ -1347,9 +1342,8 @@ remote_ra_fail(const char *node_name)
* </downed>
* </pseudo_event>
*/
-#define XPATH_PSEUDO_FENCE "/" XML_GRAPH_TAG_PSEUDO_EVENT \
- "[@" XML_LRM_ATTR_TASK "='stonith']/" XML_GRAPH_TAG_DOWNED \
- "/" XML_CIB_TAG_NODE
+#define XPATH_PSEUDO_FENCE "/" PCMK__XE_PSEUDO_EVENT \
+ "[@" PCMK_XA_OPERATION "='stonith']/" PCMK__XE_DOWNED "/" PCMK_XE_NODE
/*!
* \internal
@@ -1380,7 +1374,7 @@ remote_ra_process_pseudo(xmlNode *xml)
* recovered.
*/
if (result) {
- const char *remote = ID(result);
+ const char *remote = pcmk__xe_id(result);
if (remote) {
remote_node_down(remote, DOWN_ERASE_LRM);
@@ -1398,13 +1392,13 @@ remote_ra_maintenance(lrm_state_t * lrm_state, gboolean maintenance)
crm_node_t *node;
call_opt = crmd_cib_smart_opt();
- node = crm_remote_peer_get(lrm_state->node_name);
+ node = pcmk__cluster_lookup_remote_node(lrm_state->node_name);
CRM_CHECK(node != NULL, return);
- update = create_xml_node(NULL, XML_CIB_TAG_STATUS);
+ update = pcmk__xe_create(NULL, PCMK_XE_STATUS);
state = create_node_state_update(node, node_update_none, update,
__func__);
- crm_xml_add(state, XML_NODE_IS_MAINTENANCE, maintenance?"1":"0");
- if (controld_update_cib(XML_CIB_TAG_STATUS, update, call_opt,
+ crm_xml_add(state, PCMK__XA_NODE_IN_MAINTENANCE, (maintenance? "1" : "0"));
+ if (controld_update_cib(PCMK_XE_STATUS, update, call_opt,
NULL) == pcmk_rc_ok) {
/* TODO: still not 100% sure that async update will succeed ... */
if (maintenance) {
@@ -1416,9 +1410,9 @@ remote_ra_maintenance(lrm_state_t * lrm_state, gboolean maintenance)
free_xml(update);
}
-#define XPATH_PSEUDO_MAINTENANCE "//" XML_GRAPH_TAG_PSEUDO_EVENT \
- "[@" XML_LRM_ATTR_TASK "='" PCMK_ACTION_MAINTENANCE_NODES "']/" \
- XML_GRAPH_TAG_MAINTENANCE
+#define XPATH_PSEUDO_MAINTENANCE "//" PCMK__XE_PSEUDO_EVENT \
+ "[@" PCMK_XA_OPERATION "='" PCMK_ACTION_MAINTENANCE_NODES "']/" \
+ PCMK__XE_MAINTENANCE
/*!
* \internal
@@ -1435,25 +1429,29 @@ remote_ra_process_maintenance_nodes(xmlNode *xml)
xmlNode *node;
int cnt = 0, cnt_remote = 0;
- for (node = first_named_child(getXpathResult(search, 0),
- XML_CIB_TAG_NODE);
- node != NULL; node = crm_next_same_xml(node)) {
+ for (node = pcmk__xe_first_child(getXpathResult(search, 0),
+ PCMK_XE_NODE, NULL, NULL);
+ node != NULL; node = pcmk__xe_next_same(node)) {
- lrm_state_t *lrm_state = lrm_state_find(ID(node));
+ lrm_state_t *lrm_state = lrm_state_find(pcmk__xe_id(node));
cnt++;
if (lrm_state && lrm_state->remote_ra_data &&
pcmk_is_set(((remote_ra_data_t *) lrm_state->remote_ra_data)->status, remote_active)) {
- int is_maint;
+
+ const char *in_maint_s = NULL;
+ int in_maint;
cnt_remote++;
- pcmk__scan_min_int(crm_element_value(node, XML_NODE_IS_MAINTENANCE),
- &is_maint, 0);
- remote_ra_maintenance(lrm_state, is_maint);
+ in_maint_s = crm_element_value(node,
+ PCMK__XA_NODE_IN_MAINTENANCE);
+ pcmk__scan_min_int(in_maint_s, &in_maint, 0);
+ remote_ra_maintenance(lrm_state, in_maint);
}
}
- crm_trace("Action holds %d nodes (%d remotes found) "
- "adjusting maintenance-mode", cnt, cnt_remote);
+ crm_trace("Action holds %d nodes (%d remotes found) adjusting "
+ PCMK_OPT_MAINTENANCE_MODE,
+ cnt, cnt_remote);
}
freeXpathObject(search);
}
diff --git a/daemons/controld/controld_schedulerd.c b/daemons/controld/controld_schedulerd.c
index 8aca83f..3081304 100644
--- a/daemons/controld/controld_schedulerd.c
+++ b/daemons/controld/controld_schedulerd.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,6 @@
#include <crm/cluster.h>
#include <crm/common/xml.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml_internal.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_schedulerd.h>
@@ -65,7 +64,7 @@ save_cib_contents(xmlNode *msg, int call_id, int rc, xmlNode *output,
if (rc == pcmk_ok) {
char *filename = crm_strdup_printf(PE_STATE_DIR "/pe-core-%s.bz2", id);
- if (write_xml_file(output, filename, TRUE) < 0) {
+ if (pcmk__xml_write_file(output, filename, true, NULL) != pcmk_rc_ok) {
crm_err("Could not save Cluster Information Base to %s after scheduler crash",
filename);
} else {
@@ -144,12 +143,13 @@ handle_reply(pcmk_schedulerd_api_reply_t *reply)
*
* The name of the top level element here is irrelevant. Nothing checks it.
*/
- fsa_input.msg = create_xml_node(NULL, "dummy-reply");
- crm_xml_add(fsa_input.msg, XML_ATTR_REFERENCE, msg_ref);
- crm_xml_add(fsa_input.msg, F_CRM_TGRAPH_INPUT, reply->data.graph.input);
+ fsa_input.msg = pcmk__xe_create(NULL, "dummy-reply");
+ crm_xml_add(fsa_input.msg, PCMK_XA_REFERENCE, msg_ref);
+ crm_xml_add(fsa_input.msg, PCMK__XA_CRM_TGRAPH_IN,
+ reply->data.graph.input);
- crm_data_node = create_xml_node(fsa_input.msg, F_CRM_DATA);
- add_node_copy(crm_data_node, reply->data.graph.tgraph);
+ crm_data_node = pcmk__xe_create(fsa_input.msg, PCMK__XE_CRM_XML);
+ pcmk__xml_copy(crm_data_node, reply->data.graph.tgraph);
register_fsa_input_later(C_IPC_MESSAGE, I_PE_SUCCESS, &fsa_input);
free_xml(fsa_input.msg);
@@ -378,14 +378,14 @@ force_local_option(xmlNode *xml, const char *attr_name, const char *attr_value)
char *xpath_string = NULL;
xmlXPathObjectPtr xpathObj = NULL;
- xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CRMCONFIG);
+ xpath_base = pcmk_cib_xpath_for(PCMK_XE_CRM_CONFIG);
if (xpath_base == NULL) {
- crm_err(XML_CIB_TAG_CRMCONFIG " CIB element not known (bug?)");
+ crm_err(PCMK_XE_CRM_CONFIG " CIB element not known (bug?)");
return;
}
xpath_string = crm_strdup_printf("%s//%s//nvpair[@name='%s']",
- xpath_base, XML_CIB_TAG_PROPSET,
+ xpath_base, PCMK_XE_CLUSTER_PROPERTY_SET,
attr_name);
xpathObj = xpath_search(xml, xpath_string);
max = numXpathResults(xpathObj);
@@ -393,8 +393,9 @@ force_local_option(xmlNode *xml, const char *attr_name, const char *attr_value)
for (lpc = 0; lpc < max; lpc++) {
xmlNode *match = getXpathResult(xpathObj, lpc);
- crm_trace("Forcing %s/%s = %s", ID(match), attr_name, attr_value);
- crm_xml_add(match, XML_NVPAIR_ATTR_VALUE, attr_value);
+ crm_trace("Forcing %s/%s = %s",
+ pcmk__xe_id(match), attr_name, attr_value);
+ crm_xml_add(match, PCMK_XA_VALUE, attr_value);
}
if(max == 0) {
@@ -403,32 +404,37 @@ force_local_option(xmlNode *xml, const char *attr_name, const char *attr_value)
xmlNode *cluster_property_set = NULL;
crm_trace("Creating %s-%s for %s=%s",
- CIB_OPTIONS_FIRST, attr_name, attr_name, attr_value);
+ PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, attr_name, attr_name,
+ attr_value);
- configuration = pcmk__xe_match(xml, XML_CIB_TAG_CONFIGURATION, NULL,
- NULL);
+ configuration = pcmk__xe_first_child(xml, PCMK_XE_CONFIGURATION, NULL,
+ NULL);
if (configuration == NULL) {
- configuration = create_xml_node(xml, XML_CIB_TAG_CONFIGURATION);
+ configuration = pcmk__xe_create(xml, PCMK_XE_CONFIGURATION);
}
- crm_config = pcmk__xe_match(configuration, XML_CIB_TAG_CRMCONFIG, NULL,
- NULL);
+ crm_config = pcmk__xe_first_child(configuration, PCMK_XE_CRM_CONFIG,
+ NULL, NULL);
if (crm_config == NULL) {
- crm_config = create_xml_node(configuration, XML_CIB_TAG_CRMCONFIG);
+ crm_config = pcmk__xe_create(configuration, PCMK_XE_CRM_CONFIG);
}
- cluster_property_set = pcmk__xe_match(crm_config, XML_CIB_TAG_PROPSET,
- NULL, NULL);
+ cluster_property_set =
+ pcmk__xe_first_child(crm_config, PCMK_XE_CLUSTER_PROPERTY_SET, NULL,
+ NULL);
if (cluster_property_set == NULL) {
- cluster_property_set = create_xml_node(crm_config, XML_CIB_TAG_PROPSET);
- crm_xml_add(cluster_property_set, XML_ATTR_ID, CIB_OPTIONS_FIRST);
+ cluster_property_set =
+ pcmk__xe_create(crm_config, PCMK_XE_CLUSTER_PROPERTY_SET);
+ crm_xml_add(cluster_property_set, PCMK_XA_ID,
+ PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS);
}
- xml = create_xml_node(cluster_property_set, XML_CIB_TAG_NVPAIR);
+ xml = pcmk__xe_create(cluster_property_set, PCMK_XE_NVPAIR);
- crm_xml_set_id(xml, "%s-%s", CIB_OPTIONS_FIRST, attr_name);
- crm_xml_add(xml, XML_NVPAIR_ATTR_NAME, attr_name);
- crm_xml_add(xml, XML_NVPAIR_ATTR_VALUE, attr_value);
+ crm_xml_set_id(xml, "%s-%s",
+ PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, attr_name);
+ crm_xml_add(xml, PCMK_XA_NAME, attr_name);
+ crm_xml_add(xml, PCMK_XA_VALUE, attr_value);
}
freeXpathObject(xpathObj);
}
@@ -476,16 +482,16 @@ do_pe_invoke_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
* scheduler is invoked */
pcmk__refresh_node_caches_from_cib(output);
- crm_xml_add(output, XML_ATTR_DC_UUID, controld_globals.our_uuid);
- pcmk__xe_set_bool_attr(output, XML_ATTR_HAVE_QUORUM,
+ crm_xml_add(output, PCMK_XA_DC_UUID, controld_globals.our_uuid);
+ pcmk__xe_set_bool_attr(output, PCMK_XA_HAVE_QUORUM,
pcmk_is_set(controld_globals.flags,
controld_has_quorum));
- force_local_option(output, XML_ATTR_HAVE_WATCHDOG, pcmk__btoa(watchdog));
+ force_local_option(output, PCMK_OPT_HAVE_WATCHDOG, pcmk__btoa(watchdog));
if (pcmk_is_set(controld_globals.flags, controld_ever_had_quorum)
&& !crm_have_quorum) {
- crm_xml_add_int(output, XML_ATTR_QUORUM_PANIC, 1);
+ crm_xml_add_int(output, PCMK_XA_NO_QUORUM_PANIC, 1);
}
rc = pcmk_rc2legacy(pcmk_schedulerd_api_graph(schedulerd_api, output, &ref));
@@ -498,8 +504,8 @@ do_pe_invoke_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void
CRM_ASSERT(ref != NULL);
controld_expect_sched_reply(ref);
crm_debug("Invoking the scheduler: query=%d, ref=%s, seq=%llu, "
- "quorate=%s", fsa_pe_query, controld_globals.fsa_pe_ref,
- crm_peer_seq, pcmk__btoa(pcmk_is_set(controld_globals.flags,
- controld_has_quorum)));
+ "quorate=%s",
+ fsa_pe_query, controld_globals.fsa_pe_ref, crm_peer_seq,
+ pcmk__flag_text(controld_globals.flags, controld_has_quorum));
}
}
diff --git a/daemons/controld/controld_te_actions.c b/daemons/controld/controld_te_actions.c
index fe6b744..a4c99fc 100644
--- a/daemons/controld/controld_te_actions.c
+++ b/daemons/controld/controld_te_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,6 @@
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/lrmd.h> // lrmd_event_data_t, lrmd_free_event()
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster.h>
@@ -44,7 +43,7 @@ te_start_action_timer(const pcmk__graph_t *graph, pcmk__graph_action_t *action)
static int
execute_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *pseudo)
{
- const char *task = crm_element_value(pseudo->xml, XML_LRM_ATTR_TASK);
+ const char *task = crm_element_value(pseudo->xml, PCMK_XA_OPERATION);
/* send to peers as well? */
if (pcmk__str_eq(task, PCMK_ACTION_MAINTENANCE_NODES, pcmk__str_casei)) {
@@ -62,7 +61,7 @@ execute_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *pseudo)
cmd = create_request(task, pseudo->xml, node->uname,
CRM_SYSTEM_CRMD, CRM_SYSTEM_TENGINE, NULL);
- send_cluster_message(node, crm_msg_crmd, cmd, FALSE);
+ pcmk__cluster_send_message(node, crm_msg_crmd, cmd);
free_xml(cmd);
}
@@ -73,7 +72,7 @@ execute_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *pseudo)
}
crm_debug("Pseudo-action %d (%s) fired and confirmed", pseudo->id,
- crm_element_value(pseudo->xml, XML_LRM_ATTR_TASK_KEY));
+ crm_element_value(pseudo->xml, PCMK__XA_OPERATION_KEY));
te_action_confirmed(pseudo, graph);
return pcmk_rc_ok;
}
@@ -83,7 +82,7 @@ get_target_rc(pcmk__graph_action_t *action)
{
int exit_status;
- pcmk__scan_min_int(crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC),
+ pcmk__scan_min_int(crm_meta_value(action->params, PCMK__META_OP_TARGET_RC),
&exit_status, 0);
return exit_status;
}
@@ -113,22 +112,24 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
gboolean rc = TRUE;
gboolean no_wait = FALSE;
- id = ID(action->xml);
+ const crm_node_t *node = NULL;
+
+ id = pcmk__xe_id(action->xml);
CRM_CHECK(!pcmk__str_empty(id), return EPROTO);
- task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
+ task = crm_element_value(action->xml, PCMK_XA_OPERATION);
CRM_CHECK(!pcmk__str_empty(task), return EPROTO);
- on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ on_node = crm_element_value(action->xml, PCMK__META_ON_NODE);
CRM_CHECK(!pcmk__str_empty(on_node), return pcmk_rc_node_unknown);
- router_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ router_node = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if (router_node == NULL) {
router_node = on_node;
if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_none)) {
const char *mode = crm_element_value(action->xml, PCMK__XA_MODE);
- if (pcmk__str_eq(mode, XML_TAG_CIB, pcmk__str_none)) {
+ if (pcmk__str_eq(mode, PCMK__VALUE_CIB, pcmk__str_none)) {
router_node = controld_globals.our_nodename;
}
}
@@ -139,7 +140,7 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
is_local = TRUE;
}
- value = crm_meta_value(action->params, XML_ATTR_TE_NOWAIT);
+ value = crm_meta_value(action->params, PCMK__META_OP_NO_WAIT);
if (crm_is_true(value)) {
no_wait = TRUE;
}
@@ -158,7 +159,8 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
return pcmk_rc_ok;
} else if (pcmk__str_eq(task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
- crm_node_t *peer = crm_get_peer(0, router_node);
+ crm_node_t *peer = pcmk__get_node(0, router_node, NULL,
+ pcmk__node_search_cluster_member);
pcmk__update_peer_expected(__func__, peer, CRMD_JOINSTATE_DOWN);
}
@@ -168,9 +170,11 @@ execute_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
counter = pcmk__transition_key(controld_globals.transition_graph->id,
action->id, get_target_rc(action),
controld_globals.te_uuid);
- crm_xml_add(cmd, XML_ATTR_TRANSITION_KEY, counter);
+ crm_xml_add(cmd, PCMK__XA_TRANSITION_KEY, counter);
- rc = send_cluster_message(crm_get_peer(0, router_node), crm_msg_crmd, cmd, TRUE);
+ node = pcmk__get_node(0, router_node, NULL,
+ pcmk__node_search_cluster_member);
+ rc = pcmk__cluster_send_message(node, crm_msg_crmd, cmd);
free(counter);
free_xml(cmd);
@@ -213,16 +217,17 @@ static lrmd_event_data_t *
synthesize_timeout_event(const pcmk__graph_action_t *action, int target_rc)
{
lrmd_event_data_t *op = NULL;
- const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
const char *reason = NULL;
char *dynamic_reason = NULL;
- if (pcmk__str_eq(target, get_local_node_name(), pcmk__str_casei)) {
+ if (pcmk__str_eq(target, pcmk__cluster_local_node_name(),
+ pcmk__str_casei)) {
reason = "Local executor did not return result in time";
} else {
const char *router_node = NULL;
- router_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ router_node = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if (router_node == NULL) {
router_node = target;
}
@@ -254,18 +259,21 @@ controld_record_action_event(pcmk__graph_action_t *action,
int rc = pcmk_ok;
const char *rsc_id = NULL;
- const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
- const char *task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
- const char *target_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
+ const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
+ const char *task_uuid = crm_element_value(action->xml,
+ PCMK__XA_OPERATION_KEY);
+ const char *target_uuid = crm_element_value(action->xml,
+ PCMK__META_ON_NODE_UUID);
int target_rc = get_target_rc(action);
- action_rsc = find_xml_node(action->xml, XML_CIB_TAG_RESOURCE, TRUE);
+ action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
+ NULL);
if (action_rsc == NULL) {
return;
}
- rsc_id = ID(action_rsc);
+ rsc_id = pcmk__xe_id(action_rsc);
CRM_CHECK(rsc_id != NULL,
crm_log_xml_err(action->xml, "Bad:action"); return);
@@ -278,27 +286,27 @@ controld_record_action_event(pcmk__graph_action_t *action,
<lrm_resource id="rsc2" last_op="start" op_code="0" target="hadev"/>
*/
- state = create_xml_node(NULL, XML_CIB_TAG_STATE);
+ state = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE);
- crm_xml_add(state, XML_ATTR_ID, target_uuid);
- crm_xml_add(state, XML_ATTR_UNAME, target);
+ crm_xml_add(state, PCMK_XA_ID, target_uuid);
+ crm_xml_add(state, PCMK_XA_UNAME, target);
- rsc = create_xml_node(state, XML_CIB_TAG_LRM);
- crm_xml_add(rsc, XML_ATTR_ID, target_uuid);
+ rsc = pcmk__xe_create(state, PCMK__XE_LRM);
+ crm_xml_add(rsc, PCMK_XA_ID, target_uuid);
- rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCES);
- rsc = create_xml_node(rsc, XML_LRM_TAG_RESOURCE);
- crm_xml_add(rsc, XML_ATTR_ID, rsc_id);
+ rsc = pcmk__xe_create(rsc, PCMK__XE_LRM_RESOURCES);
+ rsc = pcmk__xe_create(rsc, PCMK__XE_LRM_RESOURCE);
+ crm_xml_add(rsc, PCMK_XA_ID, rsc_id);
- crm_copy_xml_element(action_rsc, rsc, XML_ATTR_TYPE);
- crm_copy_xml_element(action_rsc, rsc, XML_AGENT_ATTR_CLASS);
- crm_copy_xml_element(action_rsc, rsc, XML_AGENT_ATTR_PROVIDER);
+ crm_copy_xml_element(action_rsc, rsc, PCMK_XA_TYPE);
+ crm_copy_xml_element(action_rsc, rsc, PCMK_XA_CLASS);
+ crm_copy_xml_element(action_rsc, rsc, PCMK_XA_PROVIDER);
pcmk__create_history_xml(rsc, op, CRM_FEATURE_SET, target_rc, target,
__func__);
- rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_STATUS, state,
+ rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_STATUS, state,
cib_scope_local);
fsa_register_cib_callback(rc, NULL, cib_action_updated);
free_xml(state);
@@ -313,8 +321,9 @@ controld_record_action_timeout(pcmk__graph_action_t *action)
{
lrmd_event_data_t *op = NULL;
- const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
- const char *task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
+ const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
+ const char *task_uuid = crm_element_value(action->xml,
+ PCMK__XA_OPERATION_KEY);
int target_rc = get_target_rc(action);
@@ -362,17 +371,17 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
CRM_ASSERT(action->xml != NULL);
pcmk__clear_graph_action_flags(action, pcmk__graph_action_executed);
- on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ on_node = crm_element_value(action->xml, PCMK__META_ON_NODE);
CRM_CHECK(!pcmk__str_empty(on_node),
crm_err("Corrupted command(id=%s) %s: no node",
- ID(action->xml), pcmk__s(task, "without task"));
+ pcmk__xe_id(action->xml), pcmk__s(task, "without task"));
return pcmk_rc_node_unknown);
rsc_op = action->xml;
- task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
- task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
- router_node = crm_element_value(rsc_op, XML_LRM_ATTR_ROUTER_NODE);
+ task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
+ task_uuid = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
+ router_node = crm_element_value(rsc_op, PCMK__XA_ROUTER_NODE);
if (!router_node) {
router_node = on_node;
@@ -381,14 +390,14 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
counter = pcmk__transition_key(controld_globals.transition_graph->id,
action->id, get_target_rc(action),
controld_globals.te_uuid);
- crm_xml_add(rsc_op, XML_ATTR_TRANSITION_KEY, counter);
+ crm_xml_add(rsc_op, PCMK__XA_TRANSITION_KEY, counter);
if (pcmk__str_eq(router_node, controld_globals.our_nodename,
pcmk__str_casei)) {
is_local = TRUE;
}
- value = crm_meta_value(action->params, XML_ATTR_TE_NOWAIT);
+ value = crm_meta_value(action->params, PCMK__META_OP_NO_WAIT);
if (crm_is_true(value)) {
no_wait = TRUE;
}
@@ -421,7 +430,11 @@ execute_rsc_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
I_NULL, &msg);
} else {
- rc = send_cluster_message(crm_get_peer(0, router_node), crm_msg_lrmd, cmd, TRUE);
+ const crm_node_t *node =
+ pcmk__get_node(0, router_node, NULL,
+ pcmk__node_search_cluster_member);
+
+ rc = pcmk__cluster_send_message(node, crm_msg_lrmd, cmd);
}
free(counter);
@@ -500,8 +513,8 @@ te_update_job_count_on(const char *target, int offset, bool migrate)
r = g_hash_table_lookup(te_targets, target);
if(r == NULL) {
- r = calloc(1, sizeof(struct te_peer_s));
- r->name = strdup(target);
+ r = pcmk__assert_alloc(1, sizeof(struct te_peer_s));
+ r->name = pcmk__str_copy(target);
g_hash_table_insert(te_targets, r->name, r);
}
@@ -515,8 +528,8 @@ te_update_job_count_on(const char *target, int offset, bool migrate)
static void
te_update_job_count(pcmk__graph_action_t *action, int offset)
{
- const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
- const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
+ const char *target = crm_element_value(action->xml, PCMK__META_ON_NODE);
if ((action->type != pcmk__rsc_graph_action) || (target == NULL)) {
/* No limit on these */
@@ -527,19 +540,22 @@ te_update_job_count(pcmk__graph_action_t *action, int offset)
* on a remote node. For now, we count all actions occurring on a
* remote node against the job list on the cluster node hosting
* the connection resources */
- target = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ target = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if ((target == NULL)
&& pcmk__strcase_any_of(task, PCMK_ACTION_MIGRATE_TO,
PCMK_ACTION_MIGRATE_FROM, NULL)) {
- const char *t1 = crm_meta_value(action->params, XML_LRM_ATTR_MIGRATE_SOURCE);
- const char *t2 = crm_meta_value(action->params, XML_LRM_ATTR_MIGRATE_TARGET);
+
+ const char *t1 = crm_meta_value(action->params,
+ PCMK__META_MIGRATE_SOURCE);
+ const char *t2 = crm_meta_value(action->params,
+ PCMK__META_MIGRATE_TARGET);
te_update_job_count_on(t1, offset, TRUE);
te_update_job_count_on(t2, offset, TRUE);
return;
} else if (target == NULL) {
- target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ target = crm_element_value(action->xml, PCMK__META_ON_NODE);
}
te_update_job_count_on(target, offset, FALSE);
@@ -561,8 +577,8 @@ allowed_on_node(const pcmk__graph_t *graph, const pcmk__graph_action_t *action,
{
int limit = 0;
struct te_peer_s *r = NULL;
- const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
- const char *id = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
+ const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
+ const char *id = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
if(target == NULL) {
/* No limit on these */
@@ -576,8 +592,8 @@ allowed_on_node(const pcmk__graph_t *graph, const pcmk__graph_action_t *action,
limit = throttle_get_job_limit(target);
if(r == NULL) {
- r = calloc(1, sizeof(struct te_peer_s));
- r->name = strdup(target);
+ r = pcmk__assert_alloc(1, sizeof(struct te_peer_s));
+ r->name = pcmk__str_copy(target);
g_hash_table_insert(te_targets, r->name, r);
}
@@ -613,7 +629,7 @@ static bool
graph_action_allowed(pcmk__graph_t *graph, pcmk__graph_action_t *action)
{
const char *target = NULL;
- const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
+ const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
if (action->type != pcmk__rsc_graph_action) {
/* No limit on these */
@@ -624,20 +640,20 @@ graph_action_allowed(pcmk__graph_t *graph, pcmk__graph_action_t *action)
* on a remote node. For now, we count all actions occurring on a
* remote node against the job list on the cluster node hosting
* the connection resources */
- target = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ target = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if ((target == NULL)
&& pcmk__strcase_any_of(task, PCMK_ACTION_MIGRATE_TO,
PCMK_ACTION_MIGRATE_FROM, NULL)) {
- target = crm_meta_value(action->params, XML_LRM_ATTR_MIGRATE_SOURCE);
+ target = crm_meta_value(action->params, PCMK__META_MIGRATE_SOURCE);
if (!allowed_on_node(graph, action, target)) {
return false;
}
- target = crm_meta_value(action->params, XML_LRM_ATTR_MIGRATE_TARGET);
+ target = crm_meta_value(action->params, PCMK__META_MIGRATE_TARGET);
} else if (target == NULL) {
- target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
+ target = crm_element_value(action->xml, PCMK__META_ON_NODE);
}
return allowed_on_node(graph, action, target);
@@ -654,7 +670,7 @@ te_action_confirmed(pcmk__graph_action_t *action, pcmk__graph_t *graph)
{
if (!pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
if ((action->type == pcmk__rsc_graph_action)
- && (crm_element_value(action->xml, XML_LRM_ATTR_TARGET) != NULL)) {
+ && (crm_element_value(action->xml, PCMK__META_ON_NODE) != NULL)) {
te_update_job_count(action, -1);
}
pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
diff --git a/daemons/controld/controld_te_callbacks.c b/daemons/controld/controld_te_callbacks.c
index c26e757..901d44d 100644
--- a/daemons/controld/controld_te_callbacks.c
+++ b/daemons/controld/controld_te_callbacks.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,23 +14,21 @@
#include <crm/crm.h>
#include <crm/common/xml.h>
#include <crm/common/xml_internal.h>
-#include <crm/msg_xml.h>
-#include <crm/cluster.h> /* For ONLINESTATUS etc */
#include <pacemaker-controld.h>
void te_update_confirm(const char *event, xmlNode * msg);
-#define RSC_OP_PREFIX "//" XML_TAG_DIFF_ADDED "//" XML_TAG_CIB \
- "//" XML_LRM_TAG_RSC_OP "[@" XML_ATTR_ID "='"
+#define RSC_OP_PREFIX "//" PCMK__XE_DIFF_ADDED "//" PCMK_XE_CIB \
+ "//" PCMK__XE_LRM_RSC_OP "[@" PCMK_XA_ID "='"
-// An explicit shutdown-lock of 0 means the lock has been cleared
+// An explicit PCMK_OPT_SHUTDOWN_LOCK of 0 means the lock has been cleared
static bool
shutdown_lock_cleared(xmlNode *lrm_resource)
{
time_t shutdown_lock = 0;
- return (crm_element_value_epoch(lrm_resource, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
+ return (crm_element_value_epoch(lrm_resource, PCMK_OPT_SHUTDOWN_LOCK,
&shutdown_lock) == pcmk_ok)
&& (shutdown_lock == 0);
}
@@ -49,19 +47,21 @@ te_update_diff_v1(const char *event, xmlNode *diff)
"xml-patchset", diff);
if (cib__config_changed_v1(NULL, NULL, &diff)) {
- abort_transition(INFINITY, pcmk__graph_restart, "Non-status change",
- diff);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Non-status change", diff);
goto bail; /* configuration changed */
}
/* Tickets Attributes - Added/Updated */
xpathObj =
xpath_search(diff,
- "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_TICKETS);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK_XE_TICKETS);
if (numXpathResults(xpathObj) > 0) {
xmlNode *aborted = getXpathResult(xpathObj, 0);
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Ticket attribute: update", aborted);
goto bail;
@@ -71,11 +71,13 @@ te_update_diff_v1(const char *event, xmlNode *diff)
/* Tickets Attributes - Removed */
xpathObj =
xpath_search(diff,
- "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_TICKETS);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_REMOVED
+ "//" PCMK_XE_TICKETS);
if (numXpathResults(xpathObj) > 0) {
xmlNode *aborted = getXpathResult(xpathObj, 0);
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Ticket attribute: removal", aborted);
goto bail;
}
@@ -84,23 +86,24 @@ te_update_diff_v1(const char *event, xmlNode *diff)
/* Transient Attributes - Removed */
xpathObj =
xpath_search(diff,
- "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//"
- XML_TAG_TRANSIENT_NODEATTRS);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_REMOVED
+ "//" PCMK__XE_TRANSIENT_ATTRIBUTES);
if (numXpathResults(xpathObj) > 0) {
xmlNode *aborted = getXpathResult(xpathObj, 0);
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Transient attribute: removal", aborted);
goto bail;
}
freeXpathObject(xpathObj);
- // Check for lrm_resource entries
+ // Check for PCMK__XE_LRM_RESOURCE entries
xpathObj = xpath_search(diff,
- "//" F_CIB_UPDATE_RESULT
- "//" XML_TAG_DIFF_ADDED
- "//" XML_LRM_TAG_RESOURCE);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK__XE_LRM_RESOURCE);
max = numXpathResults(xpathObj);
/*
@@ -117,8 +120,8 @@ te_update_diff_v1(const char *event, xmlNode *diff)
crm_debug("Ignoring resource operation updates due to history refresh of %d resources",
max);
crm_log_xml_trace(diff, "lrm-refresh");
- abort_transition(INFINITY, pcmk__graph_restart, "History refresh",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "History refresh", NULL);
goto bail;
}
@@ -127,7 +130,7 @@ te_update_diff_v1(const char *event, xmlNode *diff)
if (shutdown_lock_cleared(lrm_resource)) {
// @TODO would be more efficient to abort once after transition done
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Shutdown lock cleared", lrm_resource);
// Still process results, so we stop timers and update failcounts
}
@@ -137,7 +140,9 @@ te_update_diff_v1(const char *event, xmlNode *diff)
/* Process operation updates */
xpathObj =
xpath_search(diff,
- "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_LRM_TAG_RSC_OP);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK__XE_LRM_RSC_OP);
max = numXpathResults(xpathObj);
if (max > 0) {
int lpc = 0;
@@ -152,7 +157,9 @@ te_update_diff_v1(const char *event, xmlNode *diff)
freeXpathObject(xpathObj);
/* Detect deleted (as opposed to replaced or added) actions - eg. crm_resource -C */
- xpathObj = xpath_search(diff, "//" XML_TAG_DIFF_REMOVED "//" XML_LRM_TAG_RSC_OP);
+ xpathObj = xpath_search(diff,
+ "//" PCMK__XE_DIFF_REMOVED
+ "//" PCMK__XE_LRM_RSC_OP);
max = numXpathResults(xpathObj);
for (lpc = 0; lpc < max; lpc++) {
const char *op_id = NULL;
@@ -162,7 +169,7 @@ te_update_diff_v1(const char *event, xmlNode *diff)
CRM_LOG_ASSERT(match != NULL);
if(match == NULL) { continue; };
- op_id = ID(match);
+ op_id = pcmk__xe_id(match);
if (rsc_op_xpath == NULL) {
rsc_op_xpath = g_string_new(RSC_OP_PREFIX);
@@ -180,13 +187,14 @@ te_update_diff_v1(const char *event, xmlNode *diff)
if (cancelled == NULL) {
crm_debug("No match for deleted action %s (%s on %s)",
(const char *) rsc_op_xpath->str, op_id, node);
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Resource op removal", match);
freeXpathObject(op_match);
goto bail;
} else {
- crm_debug("Deleted lrm_rsc_op %s on %s was for graph event %d",
+ crm_debug("Deleted " PCMK__XE_LRM_RSC_OP " %s on %s was for "
+ "graph event %d",
op_id, node, cancelled->id);
}
}
@@ -204,14 +212,14 @@ te_update_diff_v1(const char *event, xmlNode *diff)
static void
process_lrm_resource_diff(xmlNode *lrm_resource, const char *node)
{
- for (xmlNode *rsc_op = pcmk__xml_first_child(lrm_resource); rsc_op != NULL;
- rsc_op = pcmk__xml_next(rsc_op)) {
+ for (xmlNode *rsc_op = pcmk__xe_first_child(lrm_resource, NULL, NULL, NULL);
+ rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op)) {
process_graph_event(rsc_op, node);
}
if (shutdown_lock_cleared(lrm_resource)) {
// @TODO would be more efficient to abort once after transition done
- abort_transition(INFINITY, pcmk__graph_restart, "Shutdown lock cleared",
- lrm_resource);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Shutdown lock cleared", lrm_resource);
}
}
@@ -225,12 +233,12 @@ process_resource_updates(const char *node, xmlNode *xml, xmlNode *change,
return;
}
- if (pcmk__xe_is(xml, XML_CIB_TAG_LRM)) {
- xml = first_named_child(xml, XML_LRM_TAG_RESOURCES);
+ if (pcmk__xe_is(xml, PCMK__XE_LRM)) {
+ xml = pcmk__xe_first_child(xml, PCMK__XE_LRM_RESOURCES, NULL, NULL);
CRM_CHECK(xml != NULL, return);
}
- CRM_CHECK(pcmk__xe_is(xml, XML_LRM_TAG_RESOURCES), return);
+ CRM_CHECK(pcmk__xe_is(xml, PCMK__XE_LRM_RESOURCES), return);
/*
* Updates by, or in response to, TE actions will never contain updates
@@ -248,31 +256,31 @@ process_resource_updates(const char *node, xmlNode *xml, xmlNode *change,
&& (xml->children != NULL) && (xml->children->next != NULL)) {
crm_log_xml_trace(change, "lrm-refresh");
- abort_transition(INFINITY, pcmk__graph_restart, "History refresh",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "History refresh", NULL);
return;
}
- for (rsc = pcmk__xml_first_child(xml); rsc != NULL;
- rsc = pcmk__xml_next(rsc)) {
- crm_trace("Processing %s", ID(rsc));
+ for (rsc = pcmk__xe_first_child(xml, NULL, NULL, NULL); rsc != NULL;
+ rsc = pcmk__xe_next(rsc)) {
+ crm_trace("Processing %s", pcmk__xe_id(rsc));
process_lrm_resource_diff(rsc, node);
}
}
static char *extract_node_uuid(const char *xpath)
{
- char *mutable_path = strdup(xpath);
+ char *mutable_path = pcmk__str_copy(xpath);
char *node_uuid = NULL;
char *search = NULL;
char *match = NULL;
- match = strstr(mutable_path, "node_state[@" XML_ATTR_ID "=\'");
+ match = strstr(mutable_path, PCMK__XE_NODE_STATE "[@" PCMK_XA_ID "=\'");
if (match == NULL) {
free(mutable_path);
return NULL;
}
- match += strlen("node_state[@" XML_ATTR_ID "=\'");
+ match += strlen(PCMK__XE_NODE_STATE "[@" PCMK_XA_ID "=\'");
search = strchr(match, '\'');
if (search == NULL) {
@@ -281,7 +289,7 @@ static char *extract_node_uuid(const char *xpath)
}
search[0] = 0;
- node_uuid = strdup(match);
+ node_uuid = pcmk__str_copy(match);
free(mutable_path);
return node_uuid;
}
@@ -293,22 +301,25 @@ abort_unless_down(const char *xpath, const char *op, xmlNode *change,
char *node_uuid = NULL;
pcmk__graph_action_t *down = NULL;
- if(!pcmk__str_eq(op, "delete", pcmk__str_casei)) {
- abort_transition(INFINITY, pcmk__graph_restart, reason, change);
+ if (!pcmk__str_eq(op, PCMK_VALUE_DELETE, pcmk__str_none)) {
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart, reason,
+ change);
return;
}
node_uuid = extract_node_uuid(xpath);
if(node_uuid == NULL) {
crm_err("Could not extract node ID from %s", xpath);
- abort_transition(INFINITY, pcmk__graph_restart, reason, change);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart, reason,
+ change);
return;
}
down = match_down_event(node_uuid);
if (down == NULL) {
crm_trace("Not expecting %s to be down (%s)", node_uuid, xpath);
- abort_transition(INFINITY, pcmk__graph_restart, reason, change);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart, reason,
+ change);
} else {
crm_trace("Expecting changes to %s (%s)", node_uuid, xpath);
}
@@ -318,7 +329,7 @@ abort_unless_down(const char *xpath, const char *op, xmlNode *change,
static void
process_op_deletion(const char *xpath, xmlNode *change)
{
- char *mutable_key = strdup(xpath);
+ char *mutable_key = pcmk__str_copy(xpath);
char *key;
char *node_uuid;
@@ -338,7 +349,7 @@ process_op_deletion(const char *xpath, xmlNode *change)
node_uuid = extract_node_uuid(xpath);
if (confirm_cancel_action(key, node_uuid) == FALSE) {
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Resource operation removal", change);
}
free(mutable_key);
@@ -348,13 +359,13 @@ process_op_deletion(const char *xpath, xmlNode *change)
static void
process_delete_diff(const char *xpath, const char *op, xmlNode *change)
{
- if (strstr(xpath, "/" XML_LRM_TAG_RSC_OP "[")) {
+ if (strstr(xpath, "/" PCMK__XE_LRM_RSC_OP "[")) {
process_op_deletion(xpath, change);
- } else if (strstr(xpath, "/" XML_CIB_TAG_LRM "[")) {
+ } else if (strstr(xpath, "/" PCMK__XE_LRM "[")) {
abort_unless_down(xpath, op, change, "Resource state removal");
- } else if (strstr(xpath, "/" XML_CIB_TAG_STATE "[")) {
+ } else if (strstr(xpath, "/" PCMK__XE_NODE_STATE "[")) {
abort_unless_down(xpath, op, change, "Node state removal");
} else {
@@ -366,17 +377,18 @@ static void
process_node_state_diff(xmlNode *state, xmlNode *change, const char *op,
const char *xpath)
{
- xmlNode *lrm = first_named_child(state, XML_CIB_TAG_LRM);
+ xmlNode *lrm = pcmk__xe_first_child(state, PCMK__XE_LRM, NULL, NULL);
- process_resource_updates(ID(state), lrm, change, op, xpath);
+ process_resource_updates(pcmk__xe_id(state), lrm, change, op, xpath);
}
static void
process_status_diff(xmlNode *status, xmlNode *change, const char *op,
const char *xpath)
{
- for (xmlNode *state = pcmk__xml_first_child(status); state != NULL;
- state = pcmk__xml_next(state)) {
+ for (xmlNode *state = pcmk__xe_first_child(status, NULL, NULL, NULL);
+ state != NULL; state = pcmk__xe_next(state)) {
+
process_node_state_diff(state, change, op, xpath);
}
}
@@ -385,144 +397,152 @@ static void
process_cib_diff(xmlNode *cib, xmlNode *change, const char *op,
const char *xpath)
{
- xmlNode *status = first_named_child(cib, XML_CIB_TAG_STATUS);
- xmlNode *config = first_named_child(cib, XML_CIB_TAG_CONFIGURATION);
+ xmlNode *status = pcmk__xe_first_child(cib, PCMK_XE_STATUS, NULL, NULL);
+ xmlNode *config = pcmk__xe_first_child(cib, PCMK_XE_CONFIGURATION, NULL,
+ NULL);
if (status) {
process_status_diff(status, change, op, xpath);
}
if (config) {
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Non-status-only change", change);
}
}
-static void
-te_update_diff_v2(xmlNode *diff)
+static int
+te_update_diff_element_v2(xmlNode *change, void *userdata)
{
- crm_log_xml_trace(diff, "Patch:Raw");
-
- for (xmlNode *change = pcmk__xml_first_child(diff); change != NULL;
- change = pcmk__xml_next(change)) {
-
- xmlNode *match = NULL;
- const char *name = NULL;
- const char *xpath = crm_element_value(change, XML_DIFF_PATH);
-
- // Possible ops: create, modify, delete, move
- const char *op = crm_element_value(change, XML_DIFF_OP);
-
- // Ignore uninteresting updates
- if (op == NULL) {
- continue;
-
- } else if (xpath == NULL) {
- crm_trace("Ignoring %s change for version field", op);
- continue;
-
- } else if ((strcmp(op, "move") == 0)
- && (strstr(xpath,
- "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
- "/" XML_CIB_TAG_RESOURCES) == NULL)) {
- /* We still need to consider moves within the resources section,
- * since they affect placement order.
- */
- crm_trace("Ignoring move change at %s", xpath);
- continue;
+ xmlNode *match = NULL;
+ const char *name = NULL;
+ const char *xpath = crm_element_value(change, PCMK_XA_PATH);
+
+ // Possible ops: create, modify, delete, move
+ const char *op = crm_element_value(change, PCMK_XA_OPERATION);
+
+ // Ignore uninteresting updates
+ if (op == NULL) {
+ return pcmk_rc_ok;
+
+ } else if (xpath == NULL) {
+ crm_trace("Ignoring %s change for version field", op);
+ return pcmk_rc_ok;
+
+ } else if ((strcmp(op, PCMK_VALUE_MOVE) == 0)
+ && (strstr(xpath,
+ "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION
+ "/" PCMK_XE_RESOURCES) == NULL)) {
+ /* We still need to consider moves within the resources section,
+ * since they affect placement order.
+ */
+ crm_trace("Ignoring move change at %s", xpath);
+ return pcmk_rc_ok;
+ }
+
+ // Find the result of create/modify ops
+ if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
+ match = change->children;
+
+ } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
+ match = pcmk__xe_first_child(change, PCMK_XE_CHANGE_RESULT, NULL, NULL);
+ if(match) {
+ match = match->children;
}
- // Find the result of create/modify ops
- if (strcmp(op, "create") == 0) {
- match = change->children;
-
- } else if (strcmp(op, "modify") == 0) {
- match = first_named_child(change, XML_DIFF_RESULT);
- if(match) {
- match = match->children;
- }
-
- } else if (!pcmk__str_any_of(op, "delete", "move", NULL)) {
- crm_warn("Ignoring malformed CIB update (%s operation on %s is unrecognized)",
- op, xpath);
- continue;
- }
+ } else if (!pcmk__str_any_of(op,
+ PCMK_VALUE_DELETE, PCMK_VALUE_MOVE,
+ NULL)) {
+ crm_warn("Ignoring malformed CIB update (%s operation on %s is unrecognized)",
+ op, xpath);
+ return pcmk_rc_ok;
+ }
- if (match) {
- if (match->type == XML_COMMENT_NODE) {
- crm_trace("Ignoring %s operation for comment at %s", op, xpath);
- continue;
- }
- name = (const char *)match->name;
+ if (match) {
+ if (match->type == XML_COMMENT_NODE) {
+ crm_trace("Ignoring %s operation for comment at %s", op, xpath);
+ return pcmk_rc_ok;
}
+ name = (const char *)match->name;
+ }
- crm_trace("Handling %s operation for %s%s%s",
- op, (xpath? xpath : "CIB"),
- (name? " matched by " : ""), (name? name : ""));
+ crm_trace("Handling %s operation for %s%s%s",
+ op, (xpath? xpath : "CIB"),
+ (name? " matched by " : ""), (name? name : ""));
- if (strstr(xpath, "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION)) {
- abort_transition(INFINITY, pcmk__graph_restart,
- "Configuration change", change);
- break; // Won't be packaged with operation results we may be waiting for
+ if (strstr(xpath, "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION)) {
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Configuration change", change);
+ return pcmk_rc_cib_modified; // Won't be packaged with operation results we may be waiting for
- } else if (strstr(xpath, "/" XML_CIB_TAG_TICKETS)
- || pcmk__str_eq(name, XML_CIB_TAG_TICKETS, pcmk__str_none)) {
- abort_transition(INFINITY, pcmk__graph_restart,
- "Ticket attribute change", change);
- break; // Won't be packaged with operation results we may be waiting for
+ } else if (strstr(xpath, "/" PCMK_XE_TICKETS)
+ || pcmk__str_eq(name, PCMK_XE_TICKETS, pcmk__str_none)) {
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Ticket attribute change", change);
+ return pcmk_rc_cib_modified; // Won't be packaged with operation results we may be waiting for
- } else if (strstr(xpath, "/" XML_TAG_TRANSIENT_NODEATTRS "[")
- || pcmk__str_eq(name, XML_TAG_TRANSIENT_NODEATTRS,
- pcmk__str_none)) {
- abort_unless_down(xpath, op, change, "Transient attribute change");
- break; // Won't be packaged with operation results we may be waiting for
+ } else if (strstr(xpath, "/" PCMK__XE_TRANSIENT_ATTRIBUTES "[")
+ || pcmk__str_eq(name, PCMK__XE_TRANSIENT_ATTRIBUTES,
+ pcmk__str_none)) {
+ abort_unless_down(xpath, op, change, "Transient attribute change");
+ return pcmk_rc_cib_modified; // Won't be packaged with operation results we may be waiting for
- } else if (strcmp(op, "delete") == 0) {
- process_delete_diff(xpath, op, change);
+ } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
+ process_delete_diff(xpath, op, change);
- } else if (name == NULL) {
- crm_warn("Ignoring malformed CIB update (%s at %s has no result)",
- op, xpath);
+ } else if (name == NULL) {
+ crm_warn("Ignoring malformed CIB update (%s at %s has no result)",
+ op, xpath);
- } else if (strcmp(name, XML_TAG_CIB) == 0) {
- process_cib_diff(match, change, op, xpath);
+ } else if (strcmp(name, PCMK_XE_CIB) == 0) {
+ process_cib_diff(match, change, op, xpath);
- } else if (strcmp(name, XML_CIB_TAG_STATUS) == 0) {
- process_status_diff(match, change, op, xpath);
+ } else if (strcmp(name, PCMK_XE_STATUS) == 0) {
+ process_status_diff(match, change, op, xpath);
- } else if (strcmp(name, XML_CIB_TAG_STATE) == 0) {
- process_node_state_diff(match, change, op, xpath);
+ } else if (strcmp(name, PCMK__XE_NODE_STATE) == 0) {
+ process_node_state_diff(match, change, op, xpath);
- } else if (strcmp(name, XML_CIB_TAG_LRM) == 0) {
- process_resource_updates(ID(match), match, change, op, xpath);
+ } else if (strcmp(name, PCMK__XE_LRM) == 0) {
+ process_resource_updates(pcmk__xe_id(match), match, change, op,
+ xpath);
- } else if (strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ } else if (strcmp(name, PCMK__XE_LRM_RESOURCES) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- process_resource_updates(local_node, match, change, op, xpath);
- free(local_node);
+ process_resource_updates(local_node, match, change, op, xpath);
+ free(local_node);
- } else if (strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ } else if (strcmp(name, PCMK__XE_LRM_RESOURCE) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- process_lrm_resource_diff(match, local_node);
- free(local_node);
+ process_lrm_resource_diff(match, local_node);
+ free(local_node);
- } else if (strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ } else if (strcmp(name, PCMK__XE_LRM_RSC_OP) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- process_graph_event(match, local_node);
- free(local_node);
+ process_graph_event(match, local_node);
+ free(local_node);
- } else {
- crm_warn("Ignoring malformed CIB update (%s at %s has unrecognized result %s)",
- op, xpath, name);
- }
+ } else {
+ crm_warn("Ignoring malformed CIB update (%s at %s has unrecognized result %s)",
+ op, xpath, name);
}
+
+ return pcmk_rc_ok;
+}
+
+static void
+te_update_diff_v2(xmlNode *diff)
+{
+ crm_log_xml_trace(diff, "Patch:Raw");
+ pcmk__xe_foreach_child(diff, NULL, te_update_diff_element_v2, NULL);
}
void
te_update_diff(const char *event, xmlNode * msg)
{
+ xmlNode *wrapper = NULL;
xmlNode *diff = NULL;
const char *op = NULL;
int rc = -EINVAL;
@@ -531,7 +551,7 @@ te_update_diff(const char *event, xmlNode * msg)
int p_del[] = { 0, 0, 0 };
CRM_CHECK(msg != NULL, return);
- crm_element_value_int(msg, F_CIB_RC, &rc);
+ crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
if (controld_globals.transition_graph == NULL) {
crm_trace("No graph");
@@ -550,8 +570,10 @@ te_update_diff(const char *event, xmlNode * msg)
return;
}
- op = crm_element_value(msg, F_CIB_OPERATION);
- diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+ op = crm_element_value(msg, PCMK__XA_CIB_OP);
+
+ wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT, NULL, NULL);
+ diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
xml_patch_versions(diff, p_add, p_del);
crm_debug("Processing (%s) diff: %d.%d.%d -> %d.%d.%d (%s)", op,
@@ -583,7 +605,7 @@ process_te_message(xmlNode * msg, xmlNode * xml_data)
CRM_CHECK(msg != NULL, return);
// Transition requests must specify transition engine as subsystem
- value = crm_element_value(msg, F_CRM_SYS_TO);
+ value = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
if (pcmk__str_empty(value)
|| !pcmk__str_eq(value, CRM_SYSTEM_TENGINE, pcmk__str_none)) {
crm_info("Received invalid transition request: subsystem '%s' not '"
@@ -592,7 +614,7 @@ process_te_message(xmlNode * msg, xmlNode * xml_data)
}
// Only the lrm_invoke command is supported as a transition request
- value = crm_element_value(msg, F_CRM_TASK);
+ value = crm_element_value(msg, PCMK__XA_CRM_TASK);
if (!pcmk__str_eq(value, CRM_OP_INVOKE_LRM, pcmk__str_none)) {
crm_info("Received invalid transition request: command '%s' not '"
CRM_OP_INVOKE_LRM "'", pcmk__s(value, ""));
@@ -600,7 +622,7 @@ process_te_message(xmlNode * msg, xmlNode * xml_data)
}
// Transition requests must be marked as coming from the executor
- value = crm_element_value(msg, F_CRM_SYS_FROM);
+ value = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
if (!pcmk__str_eq(value, CRM_SYSTEM_LRMD, pcmk__str_none)) {
crm_info("Received invalid transition request: from '%s' not '"
CRM_SYSTEM_LRMD "'", pcmk__s(value, ""));
@@ -608,10 +630,10 @@ process_te_message(xmlNode * msg, xmlNode * xml_data)
}
crm_debug("Processing transition request with ref='%s' origin='%s'",
- pcmk__s(crm_element_value(msg, F_CRM_REFERENCE), ""),
- pcmk__s(crm_element_value(msg, F_ORIG), ""));
+ pcmk__s(crm_element_value(msg, PCMK_XA_REFERENCE), ""),
+ pcmk__s(crm_element_value(msg, PCMK__XA_SRC), ""));
- xpathObj = xpath_search(xml_data, "//" XML_LRM_TAG_RSC_OP);
+ xpathObj = xpath_search(xml_data, "//" PCMK__XE_LRM_RSC_OP);
nmatches = numXpathResults(xpathObj);
if (nmatches == 0) {
crm_err("Received transition request with no results (bug?)");
@@ -653,9 +675,9 @@ action_timer_callback(gpointer data)
stop_te_timer(action);
- task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
- on_node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
- via_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ task = crm_element_value(action->xml, PCMK_XA_OPERATION);
+ on_node = crm_element_value(action->xml, PCMK__META_ON_NODE);
+ via_node = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if (controld_globals.transition_graph->complete) {
crm_notice("Node %s did not send %s result (via %s) within %dms "
@@ -666,7 +688,7 @@ action_timer_callback(gpointer data)
/* fail the action */
crm_err("Node %s did not send %s result (via %s) within %dms "
- "(action timeout plus cluster-delay)",
+ "(action timeout plus " PCMK_OPT_CLUSTER_DELAY ")",
(on_node? on_node : ""), (task? task : "unknown action"),
(via_node? via_node : "controller"),
(action->timeout
@@ -676,7 +698,8 @@ action_timer_callback(gpointer data)
pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
te_action_confirmed(action, controld_globals.transition_graph);
- abort_transition(INFINITY, pcmk__graph_restart, "Action lost", NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Action lost", NULL);
// Record timeout in the CIB if appropriate
if ((action->type == pcmk__rsc_graph_action)
diff --git a/daemons/controld/controld_te_events.c b/daemons/controld/controld_te_events.c
index 28977c0..49c09f6 100644
--- a/daemons/controld/controld_te_events.c
+++ b/daemons/controld/controld_te_events.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,12 +12,11 @@
#include <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <pacemaker-controld.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <crm/common/ipc_attrd_internal.h>
/*!
@@ -109,17 +108,22 @@ fail_incompletable_actions(pcmk__graph_t *graph, const char *down_node)
|| pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
continue;
} else if (action->type == pcmk__cluster_graph_action) {
- const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
+ const char *task = crm_element_value(action->xml,
+ PCMK_XA_OPERATION);
if (pcmk__str_eq(task, PCMK_ACTION_STONITH, pcmk__str_casei)) {
continue;
}
}
- target_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
- router = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
+ target_uuid = crm_element_value(action->xml,
+ PCMK__META_ON_NODE_UUID);
+ router = crm_element_value(action->xml, PCMK__XA_ROUTER_NODE);
if (router) {
- crm_node_t *node = crm_get_peer(0, router);
+ const crm_node_t *node =
+ pcmk__get_node(0, router, NULL,
+ pcmk__node_search_cluster_member);
+
if (node) {
router_uuid = node->uuid;
}
@@ -134,10 +138,15 @@ fail_incompletable_actions(pcmk__graph_t *graph, const char *down_node)
if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
crm_notice("Action %d (%s) was pending on %s (offline)",
- action->id, crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY), down_node);
+ action->id,
+ crm_element_value(action->xml,
+ PCMK__XA_OPERATION_KEY),
+ down_node);
} else {
crm_info("Action %d (%s) is scheduled for %s (offline)",
- action->id, crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY), down_node);
+ action->id,
+ crm_element_value(action->xml, PCMK__XA_OPERATION_KEY),
+ down_node);
}
}
}
@@ -145,8 +154,8 @@ fail_incompletable_actions(pcmk__graph_t *graph, const char *down_node)
if (last_action != NULL) {
crm_info("Node %s shutdown resulted in un-runnable actions", down_node);
- abort_transition(INFINITY, pcmk__graph_restart, "Node failure",
- last_action);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Node failure", last_action);
return TRUE;
}
@@ -176,9 +185,9 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
char *rsc_id = NULL;
const char *value = NULL;
- const char *id = crm_element_value(event, XML_LRM_ATTR_TASK_KEY);
- const char *on_uname = crm_peer_uname(event_node_uuid);
- const char *origin = crm_element_value(event, XML_ATTR_ORIGIN);
+ const char *id = crm_element_value(event, PCMK__XA_OPERATION_KEY);
+ const char *on_uname = pcmk__node_name_from_uuid(event_node_uuid);
+ const char *origin = crm_element_value(event, PCMK_XA_CRM_DEBUG_ORIGIN);
// Nothing needs to be done for success or status refresh
if (rc == target_rc) {
@@ -192,7 +201,7 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
/* Sanity check */
CRM_CHECK(on_uname != NULL, return TRUE);
CRM_CHECK(parse_op_key(id, &rsc_id, &task, &interval_ms),
- crm_err("Couldn't parse: %s", ID(event)); goto bail);
+ crm_err("Couldn't parse: %s", pcmk__xe_id(event)); goto bail);
/* Decide whether update is necessary and what value to use */
if ((interval_ms > 0)
@@ -203,12 +212,12 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
} else if (pcmk__str_eq(task, PCMK_ACTION_START, pcmk__str_none)) {
do_update = TRUE;
value = pcmk__s(controld_globals.transition_graph->failed_start_offset,
- CRM_INFINITY_S);
+ PCMK_VALUE_INFINITY);
} else if (pcmk__str_eq(task, PCMK_ACTION_STOP, pcmk__str_none)) {
do_update = TRUE;
value = pcmk__s(controld_globals.transition_graph->failed_stop_offset,
- CRM_INFINITY_S);
+ PCMK_VALUE_INFINITY);
}
if (do_update) {
@@ -224,7 +233,7 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
// Fail count will be either incremented or set to infinity
if (!pcmk_str_is_infinity(value)) {
- value = XML_NVPAIR_ATTR_VALUE "++";
+ value = PCMK_XA_VALUE "++";
}
if (g_hash_table_lookup(crm_remote_peer_cache, event_node_uuid)) {
@@ -237,8 +246,7 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
/* Update the fail count, if we're not ignoring failures */
if (!ignore_failures) {
- fail_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
- CRM_ASSERT(fail_pair != NULL);
+ fail_pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t));
fail_name = pcmk__failcount_name(rsc_id, task, interval_ms);
fail_pair->name = fail_name;
@@ -251,8 +259,7 @@ update_failcount(const xmlNode *event, const char *event_node_uuid, int rc,
/* Update the last failure time (even if we're ignoring failures,
* so that failure can still be detected and shown, e.g. by crm_mon)
*/
- last_pair = calloc(1, sizeof(pcmk__attrd_query_pair_t));
- CRM_ASSERT(last_pair != NULL);
+ last_pair = pcmk__assert_alloc(1, sizeof(pcmk__attrd_query_pair_t));
last_name = pcmk__lastfailure_name(rsc_id, task, interval_ms);
last_pair->name = last_name;
@@ -313,18 +320,18 @@ get_cancel_action(const char *id, const char *node)
const char *target = NULL;
pcmk__graph_action_t *action = (pcmk__graph_action_t *) gIter2->data;
- task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
+ task = crm_element_value(action->xml, PCMK_XA_OPERATION);
if (!pcmk__str_eq(PCMK_ACTION_CANCEL, task, pcmk__str_casei)) {
continue;
}
- task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
+ task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
if (!pcmk__str_eq(task, id, pcmk__str_casei)) {
crm_trace("Wrong key %s for %s on %s", task, id, node);
continue;
}
- target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);
+ target = crm_element_value(action->xml, PCMK__META_ON_NODE_UUID);
if (node && !pcmk__str_eq(target, node, pcmk__str_casei)) {
crm_trace("Wrong node %s for %s on %s", target, id, node);
continue;
@@ -348,8 +355,8 @@ confirm_cancel_action(const char *id, const char *node_id)
if (cancel == NULL) {
return FALSE;
}
- op_key = crm_element_value(cancel->xml, XML_LRM_ATTR_TASK_KEY);
- node_name = crm_element_value(cancel->xml, XML_LRM_ATTR_TARGET);
+ op_key = crm_element_value(cancel->xml, PCMK__XA_OPERATION_KEY);
+ node_name = crm_element_value(cancel->xml, PCMK__META_ON_NODE);
stop_te_timer(cancel);
te_action_confirmed(cancel, controld_globals.transition_graph);
@@ -360,8 +367,8 @@ confirm_cancel_action(const char *id, const char *node_id)
}
/* downed nodes are listed like: <downed> <node id="UUID1" /> ... </downed> */
-#define XPATH_DOWNED "//" XML_GRAPH_TAG_DOWNED \
- "/" XML_CIB_TAG_NODE "[@" XML_ATTR_ID "='%s']"
+#define XPATH_DOWNED "//" PCMK__XE_DOWNED \
+ "/" PCMK_XE_NODE "[@" PCMK_XA_ID "='%s']"
/*!
* \brief Find a transition event that would have made a specified node down
@@ -405,7 +412,8 @@ match_down_event(const char *target)
if (match != NULL) {
crm_debug("Shutdown action %d (%s) found for node %s", match->id,
- crm_element_value(match->xml, XML_LRM_ATTR_TASK_KEY), target);
+ crm_element_value(match->xml, PCMK__XA_OPERATION_KEY),
+ target);
} else {
crm_debug("No reason to expect node %s to be down", target);
}
@@ -434,20 +442,20 @@ process_graph_event(xmlNode *event, const char *event_node)
<lrm_rsc_op id="rsc_east-05_last_0" operation_key="rsc_east-05_monitor_0" operation="monitor" crm-debug-origin="do_update_resource" crm_feature_set="3.0.6" transition-key="9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" transition-magic="0:7;9:2:7:be2e97d9-05e2-439d-863e-48f7aecab2aa" call-id="17" rc-code="7" op-status="0" interval="0" last-rc-change="1355361636" exec-time="128" queue-time="0" op-digest="c81f5f40b1c9e859c992e800b1aa6972"/>
*/
- magic = crm_element_value(event, XML_ATTR_TRANSITION_KEY);
+ magic = crm_element_value(event, PCMK__XA_TRANSITION_KEY);
if (magic == NULL) {
/* non-change */
return;
}
- crm_element_value_int(event, XML_LRM_ATTR_OPSTATUS, &status);
+ crm_element_value_int(event, PCMK__XA_OP_STATUS, &status);
if (status == PCMK_EXEC_PENDING) {
return;
}
- id = crm_element_value(event, XML_LRM_ATTR_TASK_KEY);
- crm_element_value_int(event, XML_LRM_ATTR_RC, &rc);
- crm_element_value_int(event, XML_LRM_ATTR_CALLID, &callid);
+ id = crm_element_value(event, PCMK__XA_OPERATION_KEY);
+ crm_element_value_int(event, PCMK__XA_RC_CODE, &rc);
+ crm_element_value_int(event, PCMK__XA_CALL_ID, &callid);
rc = pcmk__effective_rc(rc);
@@ -456,7 +464,8 @@ process_graph_event(xmlNode *event, const char *event_node)
// decode_transition_key() already logged the bad key
crm_err("Can't process action %s result: Incompatible versions? "
CRM_XS " call-id=%d", id, callid);
- abort_transition(INFINITY, pcmk__graph_restart, "Bad event", event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Bad event", event);
return;
}
@@ -468,14 +477,15 @@ process_graph_event(xmlNode *event, const char *event_node)
goto bail;
}
desc = "initiated outside of the cluster";
- abort_transition(INFINITY, pcmk__graph_restart, "Unexpected event",
- event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Unexpected event", event);
} else if ((action_num < 0)
|| !pcmk__str_eq(update_te_uuid, controld_globals.te_uuid,
pcmk__str_none)) {
desc = "initiated by a different DC";
- abort_transition(INFINITY, pcmk__graph_restart, "Foreign event", event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Foreign event", event);
} else if ((controld_globals.transition_graph->id != transition_num)
|| controld_globals.transition_graph->complete) {
@@ -496,16 +506,17 @@ process_graph_event(xmlNode *event, const char *event_node)
}
desc = "arrived after initial scheduling";
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Change in recurring result", event);
} else if (controld_globals.transition_graph->id != transition_num) {
desc = "arrived really late";
- abort_transition(INFINITY, pcmk__graph_restart, "Old event", event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Old event", event);
} else {
desc = "arrived late";
- abort_transition(INFINITY, pcmk__graph_restart, "Inactive graph",
- event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Inactive graph", event);
}
} else {
@@ -515,8 +526,8 @@ process_graph_event(xmlNode *event, const char *event_node)
if (action == NULL) {
// Should never happen
desc = "unknown";
- abort_transition(INFINITY, pcmk__graph_restart, "Unknown event",
- event);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Unknown event", event);
} else if (pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
/* Nothing further needs to be done if the action has already been
@@ -533,7 +544,8 @@ process_graph_event(xmlNode *event, const char *event_node)
* (This is the only case where desc == NULL.)
*/
- if (pcmk__str_eq(crm_meta_value(action->params, XML_OP_ATTR_ON_FAIL), "ignore", pcmk__str_casei)) {
+ if (pcmk__str_eq(crm_meta_value(action->params, PCMK_META_ON_FAIL),
+ PCMK_VALUE_IGNORE, pcmk__str_casei)) {
ignore_failures = TRUE;
} else if (rc != target_rc) {
@@ -553,7 +565,7 @@ process_graph_event(xmlNode *event, const char *event_node)
if (id == NULL) {
id = "unknown action";
}
- uname = crm_element_value(event, XML_LRM_ATTR_TARGET);
+ uname = crm_element_value(event, PCMK__META_ON_NODE);
if (uname == NULL) {
uname = "unknown node";
}
diff --git a/daemons/controld/controld_te_utils.c b/daemons/controld/controld_te_utils.c
index 5a9f029..3e71209 100644
--- a/daemons/controld/controld_te_utils.c
+++ b/daemons/controld/controld_te_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.
*
@@ -9,7 +9,6 @@
#include <crm_internal.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <pacemaker-controld.h>
@@ -192,8 +191,8 @@ node_pending_timer_popped(gpointer key)
return FALSE;
}
- crm_warn("Node with id '%s' pending timed out (%us) on joining the process "
- "group",
+ crm_warn("Node with " PCMK_XA_ID " '%s' pending timed out (%us) "
+ "on joining the process group",
(const char *) key, controld_globals.node_pending_timeout);
if (controld_globals.node_pending_timeout > 0) {
@@ -224,22 +223,19 @@ init_node_pending_timer(const crm_node_t *node, guint timeout)
return;
}
- crm_notice("Waiting for pending %s with id '%s' to join the process "
- "group (timeout=%us)",
+ crm_notice("Waiting for pending %s with " PCMK_XA_ID " '%s' "
+ "to join the process group (timeout=%us)",
node->uname ? node->uname : "node", node->uuid,
controld_globals.node_pending_timeout);
- node_pending_timer = calloc(1, sizeof(struct abort_timer_s));
- CRM_ASSERT(node_pending_timer != NULL);
+ key = pcmk__str_copy(node->uuid);
+ node_pending_timer = pcmk__assert_alloc(1, sizeof(struct abort_timer_s));
node_pending_timer->aborted = FALSE;
- node_pending_timer->priority = INFINITY;
+ node_pending_timer->priority = PCMK_SCORE_INFINITY;
node_pending_timer->action = pcmk__graph_restart;
node_pending_timer->text = "Node pending timed out";
- key = strdup(node->uuid);
- CRM_ASSERT(key != NULL);
-
g_hash_table_replace(node_pending_timers, key, node_pending_timer);
node_pending_timer->id = g_timeout_add_seconds(timeout,
@@ -264,8 +260,8 @@ controld_node_pending_timer(const crm_node_t *node)
long long remaining_timeout = 0;
/* If the node is not an active cluster node, is leaving the cluster, or is
- * already part of CPG, or node-pending-timeout is disabled, free any
- * node pending timer for it.
+ * already part of CPG, or PCMK_OPT_NODE_PENDING_TIMEOUT is disabled, free
+ * any node pending timer for it.
*/
if (pcmk_is_set(node->flags, crm_remote_node)
|| (node->when_member <= 1) || (node->when_online > 0)
@@ -386,7 +382,7 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action,
const xmlNode *search = NULL;
for(search = reason; search; search = search->parent) {
- if (pcmk__xe_is(search, XML_TAG_DIFF)) {
+ if (pcmk__xe_is(search, PCMK_XE_DIFF)) {
diff = search;
break;
}
@@ -395,7 +391,7 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action,
if(diff) {
xml_patch_versions(diff, add, del);
for(search = reason; search; search = search->parent) {
- if (pcmk__xe_is(search, XML_DIFF_CHANGE)) {
+ if (pcmk__xe_is(search, PCMK_XE_CHANGE)) {
change = search;
break;
}
@@ -417,21 +413,22 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action,
do_crm_log(level, "Transition %d aborted by %s.%s: %s "
CRM_XS " cib=%d.%d.%d source=%s:%d path=%s complete=%s",
controld_globals.transition_graph->id, reason->name,
- ID(reason), abort_text, add[0], add[1], add[2], fn, line,
- (const char *) local_path->str,
+ pcmk__xe_id(reason), abort_text, add[0], add[1], add[2], fn,
+ line, (const char *) local_path->str,
pcmk__btoa(controld_globals.transition_graph->complete));
g_string_free(local_path, TRUE);
} else {
- const char *op = crm_element_value(change, XML_DIFF_OP);
- const char *path = crm_element_value(change, XML_DIFF_PATH);
+ const char *op = crm_element_value(change, PCMK_XA_OPERATION);
+ const char *path = crm_element_value(change, PCMK_XA_PATH);
if(change == reason) {
- if(strcmp(op, "create") == 0) {
+ if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
reason = reason->children;
- } else if(strcmp(op, "modify") == 0) {
- reason = first_named_child(reason, XML_DIFF_RESULT);
+ } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
+ reason = pcmk__xe_first_child(reason, PCMK_XE_CHANGE_RESULT,
+ NULL, NULL);
if(reason) {
reason = reason->children;
}
@@ -439,7 +436,7 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action,
CRM_CHECK(reason != NULL, goto done);
}
- if(strcmp(op, "delete") == 0) {
+ if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
const char *shortpath = strrchr(path, '/');
do_crm_log(level, "Transition %d aborted by deletion of %s: %s "
@@ -449,40 +446,42 @@ abort_transition_graph(int abort_priority, enum pcmk__graph_next abort_action,
add[0], add[1], add[2], fn, line, path,
pcmk__btoa(controld_globals.transition_graph->complete));
- } else if (pcmk__xe_is(reason, XML_CIB_TAG_NVPAIR)) {
+ } else if (pcmk__xe_is(reason, PCMK_XE_NVPAIR)) {
do_crm_log(level, "Transition %d aborted by %s doing %s %s=%s: %s "
CRM_XS " cib=%d.%d.%d source=%s:%d path=%s complete=%s",
controld_globals.transition_graph->id,
- crm_element_value(reason, XML_ATTR_ID), op,
- crm_element_value(reason, XML_NVPAIR_ATTR_NAME),
- crm_element_value(reason, XML_NVPAIR_ATTR_VALUE),
+ crm_element_value(reason, PCMK_XA_ID), op,
+ crm_element_value(reason, PCMK_XA_NAME),
+ crm_element_value(reason, PCMK_XA_VALUE),
abort_text, add[0], add[1], add[2], fn, line, path,
pcmk__btoa(controld_globals.transition_graph->complete));
- } else if (pcmk__xe_is(reason, XML_LRM_TAG_RSC_OP)) {
- const char *magic = crm_element_value(reason, XML_ATTR_TRANSITION_MAGIC);
+ } else if (pcmk__xe_is(reason, PCMK__XE_LRM_RSC_OP)) {
+ const char *magic = crm_element_value(reason,
+ PCMK__XA_TRANSITION_MAGIC);
do_crm_log(level, "Transition %d aborted by operation %s '%s' on %s: %s "
CRM_XS " magic=%s cib=%d.%d.%d source=%s:%d complete=%s",
controld_globals.transition_graph->id,
- crm_element_value(reason, XML_LRM_ATTR_TASK_KEY), op,
- crm_element_value(reason, XML_LRM_ATTR_TARGET), abort_text,
+ crm_element_value(reason, PCMK__XA_OPERATION_KEY), op,
+ crm_element_value(reason, PCMK__META_ON_NODE),
+ abort_text,
magic, add[0], add[1], add[2], fn, line,
pcmk__btoa(controld_globals.transition_graph->complete));
} else if (pcmk__str_any_of((const char *) reason->name,
- XML_CIB_TAG_STATE, XML_CIB_TAG_NODE, NULL)) {
- const char *uname = crm_peer_uname(ID(reason));
+ PCMK__XE_NODE_STATE, PCMK_XE_NODE, NULL)) {
+ const char *uname = pcmk__node_name_from_uuid(pcmk__xe_id(reason));
do_crm_log(level, "Transition %d aborted by %s '%s' on %s: %s "
CRM_XS " cib=%d.%d.%d source=%s:%d complete=%s",
controld_globals.transition_graph->id,
- reason->name, op, pcmk__s(uname, ID(reason)),
+ reason->name, op, pcmk__s(uname, pcmk__xe_id(reason)),
abort_text, add[0], add[1], add[2], fn, line,
pcmk__btoa(controld_globals.transition_graph->complete));
} else {
- const char *id = ID(reason);
+ const char *id = pcmk__xe_id(reason);
do_crm_log(level, "Transition %d aborted by %s.%s '%s': %s "
CRM_XS " cib=%d.%d.%d source=%s:%d path=%s complete=%s",
diff --git a/daemons/controld/controld_throttle.c b/daemons/controld/controld_throttle.c
index a4775e5..08ec329 100644
--- a/daemons/controld/controld_throttle.c
+++ b/daemons/controld/controld_throttle.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2013-2021 the Pacemaker project contributors
+ * Copyright 2013-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -17,7 +17,7 @@
#include <dirent.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/cluster.h>
#include <pacemaker-controld.h>
@@ -160,7 +160,7 @@ throttle_cib_load(float *load)
}
if(fgets(buffer, sizeof(buffer), stream)) {
- char *comm = calloc(1, 256);
+ char *comm = pcmk__assert_alloc(1, 256);
char state = 0;
int rc = 0, pid = 0, ppid = 0, pgrp = 0, session = 0, tty_nr = 0, tpgid = 0;
unsigned long flags = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0, utime = 0, stime = 0;
@@ -368,10 +368,10 @@ throttle_send_command(enum throttle_state_e mode)
last = mode;
xml = create_request(CRM_OP_THROTTLE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
- crm_xml_add_int(xml, F_CRM_THROTTLE_MODE, mode);
- crm_xml_add_int(xml, F_CRM_THROTTLE_MAX, throttle_job_max);
+ crm_xml_add_int(xml, PCMK__XA_CRM_LIMIT_MODE, mode);
+ crm_xml_add_int(xml, PCMK__XA_CRM_LIMIT_MAX, throttle_job_max);
- send_cluster_message(NULL, crm_msg_crmd, xml, TRUE);
+ pcmk__cluster_send_message(NULL, crm_msg_crmd, xml);
free_xml(xml);
}
}
@@ -401,7 +401,8 @@ throttle_set_load_target(float target)
* \internal
* \brief Update the maximum number of simultaneous jobs
*
- * \param[in] preference Cluster-wide node-action-limit from the CIB
+ * \param[in] preference Cluster-wide \c PCMK_OPT_NODE_ACTION_LIMIT from the
+ * CIB
*/
static void
throttle_update_job_max(const char *preference)
@@ -416,7 +417,7 @@ throttle_update_job_max(const char *preference)
pcmk__scan_ll(preference, &max, 0LL);
}
if (max > 0) {
- throttle_job_max = (int) max;
+ throttle_job_max = (max >= INT_MAX)? INT_MAX : (int) max;
} else {
// Default is based on the number of cores detected
throttle_job_max = 2 * pcmk__procfs_num_cores();
@@ -444,13 +445,13 @@ throttle_init(void)
void
controld_configure_throttle(GHashTable *options)
{
- const char *value = g_hash_table_lookup(options, "load-threshold");
+ const char *value = g_hash_table_lookup(options, PCMK_OPT_LOAD_THRESHOLD);
if (value != NULL) {
throttle_set_load_target(strtof(value, NULL) / 100.0);
}
- value = g_hash_table_lookup(options, "node-action-limit");
+ value = g_hash_table_lookup(options, PCMK_OPT_NODE_ACTION_LIMIT);
throttle_update_job_max(value);
}
@@ -473,7 +474,7 @@ throttle_get_total_job_limit(int l)
/* Cluster-wide limit */
GHashTableIter iter;
int limit = l;
- int peers = crm_active_peers();
+ int peers = pcmk__cluster_num_active_nodes();
struct throttle_record_s *r = NULL;
g_hash_table_iter_init(&iter, throttle_records);
@@ -497,13 +498,12 @@ throttle_get_total_job_limit(int l)
}
}
if(limit == l) {
- /* crm_trace("No change to batch-limit=%d", limit); */
} else if(l == 0) {
- crm_trace("Using batch-limit=%d", limit);
+ crm_trace("Using " PCMK_OPT_BATCH_LIMIT "=%d", limit);
} else {
- crm_trace("Using batch-limit=%d instead of %d", limit, l);
+ crm_trace("Using " PCMK_OPT_BATCH_LIMIT "=%d instead of %d", limit, l);
}
return limit;
}
@@ -516,8 +516,8 @@ throttle_get_job_limit(const char *node)
r = g_hash_table_lookup(throttle_records, node);
if(r == NULL) {
- r = calloc(1, sizeof(struct throttle_record_s));
- r->node = strdup(node);
+ r = pcmk__assert_alloc(1, sizeof(struct throttle_record_s));
+ r->node = pcmk__str_copy(node);
r->mode = throttle_low;
r->max = throttle_job_max;
crm_trace("Defaulting to local values for unknown node %s", node);
@@ -552,16 +552,16 @@ throttle_update(xmlNode *xml)
int max = 0;
int mode = 0;
struct throttle_record_s *r = NULL;
- const char *from = crm_element_value(xml, F_CRM_HOST_FROM);
+ const char *from = crm_element_value(xml, PCMK__XA_SRC);
- crm_element_value_int(xml, F_CRM_THROTTLE_MODE, &mode);
- crm_element_value_int(xml, F_CRM_THROTTLE_MAX, &max);
+ crm_element_value_int(xml, PCMK__XA_CRM_LIMIT_MODE, &mode);
+ crm_element_value_int(xml, PCMK__XA_CRM_LIMIT_MAX, &max);
r = g_hash_table_lookup(throttle_records, from);
if(r == NULL) {
- r = calloc(1, sizeof(struct throttle_record_s));
- r->node = strdup(from);
+ r = pcmk__assert_alloc(1, sizeof(struct throttle_record_s));
+ r->node = pcmk__str_copy(from);
g_hash_table_insert(throttle_records, r->node, r);
}
diff --git a/daemons/controld/controld_timers.c b/daemons/controld/controld_timers.c
index a65bef5..0d387b9 100644
--- a/daemons/controld/controld_timers.c
+++ b/daemons/controld/controld_timers.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.
*
@@ -13,7 +13,7 @@
#include <stdlib.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <pacemaker-controld.h>
//! FSA mainloop timer type
@@ -38,10 +38,10 @@ static fsa_timer_t *election_timer = NULL;
//! Delay start of new transition with expectation something else might happen
static fsa_timer_t *transition_timer = NULL;
-//! join-integration-timeout
+//! \c PCMK_OPT_JOIN_INTEGRATION_TIMEOUT
static fsa_timer_t *integration_timer = NULL;
-//! join-finalization-timeout
+//! \c PCMK_OPT_JOIN_FINALIZATION_TIMEOUT
static fsa_timer_t *finalization_timer = NULL;
// Wait for DC to stop all resources and give us the all-clear to shut down
@@ -229,40 +229,13 @@ crm_timer_popped(gpointer data)
bool
controld_init_fsa_timers(void)
{
- transition_timer = calloc(1, sizeof(fsa_timer_t));
- if (transition_timer == NULL) {
- return FALSE;
- }
-
- integration_timer = calloc(1, sizeof(fsa_timer_t));
- if (integration_timer == NULL) {
- return FALSE;
- }
-
- finalization_timer = calloc(1, sizeof(fsa_timer_t));
- if (finalization_timer == NULL) {
- return FALSE;
- }
-
- election_timer = calloc(1, sizeof(fsa_timer_t));
- if (election_timer == NULL) {
- return FALSE;
- }
-
- shutdown_escalation_timer = calloc(1, sizeof(fsa_timer_t));
- if (shutdown_escalation_timer == NULL) {
- return FALSE;
- }
-
- wait_timer = calloc(1, sizeof(fsa_timer_t));
- if (wait_timer == NULL) {
- return FALSE;
- }
-
- recheck_timer = calloc(1, sizeof(fsa_timer_t));
- if (recheck_timer == NULL) {
- return FALSE;
- }
+ transition_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ integration_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ finalization_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ election_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ shutdown_escalation_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ wait_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
+ recheck_timer = pcmk__assert_alloc(1, sizeof(fsa_timer_t));
election_timer->source_id = 0;
election_timer->period_ms = 0;
@@ -332,30 +305,30 @@ controld_configure_fsa_timers(GHashTable *options)
const char *value = NULL;
// Election timer
- value = g_hash_table_lookup(options, XML_CONFIG_ATTR_DC_DEADTIME);
- election_timer->period_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_DC_DEADTIME);
+ pcmk_parse_interval_spec(value, &(election_timer->period_ms));
// Integration timer
- value = g_hash_table_lookup(options, "join-integration-timeout");
- integration_timer->period_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_JOIN_INTEGRATION_TIMEOUT);
+ pcmk_parse_interval_spec(value, &(integration_timer->period_ms));
// Finalization timer
- value = g_hash_table_lookup(options, "join-finalization-timeout");
- finalization_timer->period_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_JOIN_FINALIZATION_TIMEOUT);
+ pcmk_parse_interval_spec(value, &(finalization_timer->period_ms));
// Shutdown escalation timer
- value = g_hash_table_lookup(options, XML_CONFIG_ATTR_FORCE_QUIT);
- shutdown_escalation_timer->period_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_SHUTDOWN_ESCALATION);
+ pcmk_parse_interval_spec(value, &(shutdown_escalation_timer->period_ms));
crm_debug("Shutdown escalation occurs if DC has not responded to request "
"in %ums", shutdown_escalation_timer->period_ms);
// Transition timer
- value = g_hash_table_lookup(options, "transition-delay");
- transition_timer->period_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_TRANSITION_DELAY);
+ pcmk_parse_interval_spec(value, &(transition_timer->period_ms));
// Recheck interval
- value = g_hash_table_lookup(options, XML_CONFIG_ATTR_RECHECK);
- recheck_interval_ms = crm_parse_interval_spec(value);
+ value = g_hash_table_lookup(options, PCMK_OPT_CLUSTER_RECHECK_INTERVAL);
+ pcmk_parse_interval_spec(value, &recheck_interval_ms);
crm_debug("Re-run scheduler after %dms of inactivity", recheck_interval_ms);
}
diff --git a/daemons/controld/controld_transition.c b/daemons/controld/controld_transition.c
index 897c6d3..184253d 100644
--- a/daemons/controld/controld_transition.c
+++ b/daemons/controld/controld_transition.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-controld.h>
@@ -41,7 +40,8 @@ do_te_control(long long action,
controld_globals.transition_graph = NULL;
if (cib_conn != NULL) {
- cib_conn->cmds->del_notify_callback(cib_conn, T_CIB_DIFF_NOTIFY,
+ cib_conn->cmds->del_notify_callback(cib_conn,
+ PCMK__VALUE_CIB_DIFF_NOTIFY,
te_update_diff);
}
@@ -71,12 +71,11 @@ do_te_control(long long action,
crm_err("Could not set CIB callbacks");
init_ok = FALSE;
- } else {
- if (cib_conn->cmds->add_notify_callback(cib_conn, T_CIB_DIFF_NOTIFY,
- te_update_diff) != pcmk_ok) {
- crm_err("Could not set CIB notification callback");
- init_ok = FALSE;
- }
+ } else if (cib_conn->cmds->add_notify_callback(cib_conn,
+ PCMK__VALUE_CIB_DIFF_NOTIFY,
+ te_update_diff) != pcmk_ok) {
+ crm_err("Could not set CIB notification callback");
+ init_ok = FALSE;
}
if (init_ok) {
@@ -110,13 +109,15 @@ do_te_invoke(long long action,
if (action & A_TE_CANCEL) {
crm_debug("Cancelling the transition: %sactive",
controld_globals.transition_graph->complete? "in" : "");
- abort_transition(INFINITY, pcmk__graph_restart, "Peer Cancelled", NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Peer Cancelled", NULL);
if (!controld_globals.transition_graph->complete) {
crmd_fsa_stall(FALSE);
}
} else if (action & A_TE_HALT) {
- abort_transition(INFINITY, pcmk__graph_wait, "Peer Halt", NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_wait, "Peer Halt",
+ NULL);
if (!controld_globals.transition_graph->complete) {
crmd_fsa_stall(FALSE);
}
@@ -124,11 +125,11 @@ do_te_invoke(long long action,
} else if (action & A_TE_INVOKE) {
ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
xmlNode *graph_data = input->xml;
- const char *ref = crm_element_value(input->msg, XML_ATTR_REFERENCE);
- const char *graph_file = crm_element_value(input->msg, F_CRM_TGRAPH);
- const char *graph_input = crm_element_value(input->msg, F_CRM_TGRAPH_INPUT);
+ const char *ref = crm_element_value(input->msg, PCMK_XA_REFERENCE);
+ const char *graph_input = crm_element_value(input->msg,
+ PCMK__XA_CRM_TGRAPH_IN);
- if (graph_file == NULL && graph_data == NULL) {
+ if (graph_data == NULL) {
crm_log_xml_err(input->msg, "Bad command");
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
return;
@@ -136,8 +137,8 @@ do_te_invoke(long long action,
if (!controld_globals.transition_graph->complete) {
crm_info("Another transition is already active");
- abort_transition(INFINITY, pcmk__graph_restart, "Transition Active",
- NULL);
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
+ "Transition Active", NULL);
return;
}
@@ -147,14 +148,10 @@ do_te_invoke(long long action,
crm_info("Transition is redundant: %s expected but %s received",
pcmk__s(controld_globals.fsa_pe_ref, "no reference"),
pcmk__s(ref, "no reference"));
- abort_transition(INFINITY, pcmk__graph_restart,
+ abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
"Transition Redundant", NULL);
}
- if (graph_data == NULL && graph_file != NULL) {
- graph_data = filename2xml(graph_file);
- }
-
if (controld_is_started_transition_timer()) {
crm_debug("The transitioner wait for a transition timer");
return;
diff --git a/daemons/controld/controld_utils.c b/daemons/controld/controld_utils.c
index 9b306ee..fc0a8fd 100644
--- a/daemons/controld/controld_utils.c
+++ b/daemons/controld/controld_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.
*
@@ -14,7 +14,6 @@
#include <crm/crm.h>
#include <crm/cib.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <pacemaker-controld.h>
@@ -694,8 +693,8 @@ update_dc(xmlNode * msg)
if (msg != NULL) {
gboolean invalid = FALSE;
- dc_version = crm_element_value(msg, F_CRM_VERSION);
- welcome_from = crm_element_value(msg, F_CRM_HOST_FROM);
+ dc_version = crm_element_value(msg, PCMK_XA_VERSION);
+ welcome_from = crm_element_value(msg, PCMK__XA_SRC);
CRM_CHECK(dc_version != NULL, return FALSE);
CRM_CHECK(welcome_from != NULL, return FALSE);
@@ -734,7 +733,8 @@ update_dc(xmlNode * msg)
/* do nothing */
} else if (controld_globals.dc_name != NULL) {
- crm_node_t *dc_node = crm_get_peer(0, controld_globals.dc_name);
+ crm_node_t *dc_node = pcmk__get_node(0, controld_globals.dc_name, NULL,
+ pcmk__node_search_cluster_member);
crm_info("Set DC to %s (%s)",
controld_globals.dc_name,
@@ -828,10 +828,10 @@ get_node_id(xmlNode *lrm_rsc_op)
{
xmlNode *node = lrm_rsc_op;
- while ((node != NULL) && !pcmk__xe_is(node, XML_CIB_TAG_STATE)) {
+ while ((node != NULL) && !pcmk__xe_is(node, PCMK__XE_NODE_STATE)) {
node = node->parent;
}
CRM_CHECK(node != NULL, return NULL);
- return ID(node);
+ return pcmk__xe_id(node);
}
diff --git a/daemons/controld/pacemaker-controld.c b/daemons/controld/pacemaker-controld.c
index e4a72c2..913518b 100644
--- a/daemons/controld/pacemaker-controld.c
+++ b/daemons/controld/pacemaker-controld.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.
*
@@ -46,11 +46,23 @@ static pcmk__supported_format_t formats[] = {
{ NULL, NULL, NULL }
};
+/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
+ * crm_attribute --list-options=cluster instead of querying daemon metadata.
+ */
+static int
+controld_metadata(pcmk__output_t *out)
+{
+ return pcmk__daemon_metadata(out, "pacemaker-controld",
+ "Pacemaker controller options",
+ "Cluster options used by Pacemaker's "
+ "controller",
+ pcmk__opt_controld);
+}
+
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
{
- return pcmk__build_arg_context(args, "text (default), xml", group,
- "[metadata]");
+ return pcmk__build_arg_context(args, "text (default), xml", group, NULL);
}
int
@@ -96,8 +108,14 @@ main(int argc, char **argv)
if ((g_strv_length(processed_args) >= 2)
&& pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
- crmd_metadata();
+
initialize = false;
+ rc = controld_metadata(out);
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Unable to display metadata: %s", pcmk_rc_str(rc));
+ }
goto done;
}
@@ -178,7 +196,7 @@ crmd_init(void)
init_dotfile();
register_fsa_input(C_STARTUP, I_STARTUP, NULL);
- crm_peer_init();
+ pcmk__cluster_init_node_caches();
state = s_crmd_fsa(C_STARTUP);
if (state == S_PENDING || state == S_STARTING) {
diff --git a/daemons/controld/pacemaker-controld.h b/daemons/controld/pacemaker-controld.h
index 2334cce..ba8dc8f 100644
--- a/daemons/controld/pacemaker-controld.h
+++ b/daemons/controld/pacemaker-controld.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -28,7 +28,6 @@
# define controld_trigger_config() \
controld_trigger_config_as(__func__, __LINE__)
-void crmd_metadata(void);
void controld_trigger_config_as(const char *fn, int line);
void controld_election_init(const char *uname);
void controld_configure_election(GHashTable *options);
diff --git a/daemons/execd/Makefile.am b/daemons/execd/Makefile.am
index ab8544f..ce0e161 100644
--- a/daemons/execd/Makefile.am
+++ b/daemons/execd/Makefile.am
@@ -44,12 +44,14 @@ pacemaker_remoted_LDFLAGS = $(LDFLAGS_HARDENED_EXE)
pacemaker_remoted_LDADD = $(top_builddir)/lib/fencing/libstonithd.la
pacemaker_remoted_LDADD += $(top_builddir)/lib/services/libcrmservice.la
+pacemaker_remoted_LDADD += $(top_builddir)/lib/cib/libcib.la
pacemaker_remoted_LDADD += $(top_builddir)/lib/lrmd/liblrmd.la
pacemaker_remoted_LDADD += $(top_builddir)/lib/common/libcrmcommon.la
pacemaker_remoted_SOURCES = $(pacemaker_execd_SOURCES) \
remoted_tls.c \
remoted_pidone.c \
- remoted_proxy.c
+ remoted_proxy.c \
+ remoted_schemas.c
endif
cts_exec_helper_LDADD = $(top_builddir)/lib/pengine/libpe_status.la
diff --git a/daemons/execd/cts-exec-helper.c b/daemons/execd/cts-exec-helper.c
index 6ebbedf..b8f4407 100644
--- a/daemons/execd/cts-exec-helper.c
+++ b/daemons/execd/cts-exec-helper.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.
*
@@ -56,8 +56,8 @@ static struct {
static gboolean
interval_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.interval_ms = crm_parse_interval_spec(optarg);
- return errno == 0;
+ return pcmk_parse_interval_spec(optarg,
+ &options.interval_ms) == pcmk_rc_ok;
}
static gboolean
@@ -461,9 +461,9 @@ generate_params(void)
if (rc != pcmk_rc_ok) {
return rc;
}
- if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) {
- crm_err("Could not update CIB");
- return pcmk_rc_cib_corrupt;
+ rc = pcmk_update_configured_schema(&cib_xml_copy, false);
+ if (rc != pcmk_rc_ok) {
+ return rc;
}
// Calculate cluster status
@@ -472,7 +472,7 @@ generate_params(void)
crm_crit("Could not allocate scheduler data");
return ENOMEM;
}
- pe__set_working_set_flags(scheduler,
+ pcmk__set_scheduler_flags(scheduler,
pcmk_sched_no_counts|pcmk_sched_no_compat);
scheduler->input = cib_xml_copy;
scheduler->now = crm_time_new(NULL);
diff --git a/daemons/execd/execd_alerts.c b/daemons/execd/execd_alerts.c
index 5944d93..362f7a5 100644
--- a/daemons/execd/execd_alerts.c
+++ b/daemons/execd/execd_alerts.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2022 the Pacemaker project contributors
+ * Copyright 2016-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -17,7 +17,7 @@
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/alerts_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include "pacemaker-execd.h"
@@ -105,9 +105,11 @@ process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
{
static int alert_sequence_no = 0;
- xmlNode *alert_xml = get_xpath_object("//" F_LRMD_ALERT, request, LOG_ERR);
- const char *alert_id = crm_element_value(alert_xml, F_LRMD_ALERT_ID);
- const char *alert_path = crm_element_value(alert_xml, F_LRMD_ALERT_PATH);
+ xmlNode *alert_xml = get_xpath_object("//" PCMK__XE_LRMD_ALERT, request,
+ LOG_ERR);
+ const char *alert_id = crm_element_value(alert_xml, PCMK__XA_LRMD_ALERT_ID);
+ const char *alert_path = crm_element_value(alert_xml,
+ PCMK__XA_LRMD_ALERT_PATH);
svc_action_t *action = NULL;
int alert_timeout = 0;
int rc = pcmk_ok;
@@ -116,13 +118,14 @@ process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
if ((alert_id == NULL) || (alert_path == NULL) ||
(client == NULL) || (client->id == NULL)) { /* hint static analyzer */
- return -EINVAL;
+ rc = -EINVAL;
+ goto err;
}
if (draining_alerts) {
return pcmk_ok;
}
- crm_element_value_int(alert_xml, F_LRMD_TIMEOUT, &alert_timeout);
+ crm_element_value_int(alert_xml, PCMK__XA_LRMD_TIMEOUT, &alert_timeout);
crm_info("Executing alert %s for %s", alert_id, client->id);
@@ -130,20 +133,11 @@ process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
pcmk__add_alert_key_int(params, PCMK__alert_key_node_sequence,
++alert_sequence_no);
- cb_data = calloc(1, sizeof(struct alert_cb_s));
- if (cb_data == NULL) {
- rc = -errno;
- goto err;
- }
+ cb_data = pcmk__assert_alloc(1, sizeof(struct alert_cb_s));
- /* coverity[deref_ptr] False Positive */
- cb_data->client_id = strdup(client->id);
- if (cb_data->client_id == NULL) {
- rc = -errno;
- goto err;
- }
+ cb_data->client_id = pcmk__str_copy(client->id);
- crm_element_value_int(request, F_LRMD_CALLID, &(cb_data->call_id));
+ crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &(cb_data->call_id));
action = services_alert_create(alert_id, alert_path, alert_timeout, params,
alert_sequence_no, cb_data);
@@ -165,9 +159,7 @@ process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
err:
if (cb_data) {
- if (cb_data->client_id) {
- free(cb_data->client_id);
- }
+ free(cb_data->client_id);
free(cb_data);
}
services_action_free(action);
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index cf4503a..6b1ded1 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.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.
*
@@ -28,7 +28,7 @@
#include <crm/common/mainloop.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include "pacemaker-execd.h"
@@ -274,17 +274,17 @@ normalize_action_name(lrmd_rsc_t * rsc, const char *action)
static lrmd_rsc_t *
build_rsc_from_xml(xmlNode * msg)
{
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, msg, LOG_ERR);
lrmd_rsc_t *rsc = NULL;
- rsc = calloc(1, sizeof(lrmd_rsc_t));
+ rsc = pcmk__assert_alloc(1, sizeof(lrmd_rsc_t));
- crm_element_value_int(msg, F_LRMD_CALLOPTS, &rsc->call_opts);
+ crm_element_value_int(msg, PCMK__XA_LRMD_CALLOPT, &rsc->call_opts);
- rsc->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID);
- rsc->class = crm_element_value_copy(rsc_xml, F_LRMD_CLASS);
- rsc->provider = crm_element_value_copy(rsc_xml, F_LRMD_PROVIDER);
- rsc->type = crm_element_value_copy(rsc_xml, F_LRMD_TYPE);
+ rsc->rsc_id = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ID);
+ rsc->class = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_CLASS);
+ rsc->provider = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_PROVIDER);
+ rsc->type = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_TYPE);
rsc->work = mainloop_add_trigger(G_PRIORITY_HIGH, execute_resource_action,
rsc);
@@ -298,29 +298,33 @@ static lrmd_cmd_t *
create_lrmd_cmd(xmlNode *msg, pcmk__client_t *client)
{
int call_options = 0;
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, msg, LOG_ERR);
lrmd_cmd_t *cmd = NULL;
- cmd = calloc(1, sizeof(lrmd_cmd_t));
+ cmd = pcmk__assert_alloc(1, sizeof(lrmd_cmd_t));
- crm_element_value_int(msg, F_LRMD_CALLOPTS, &call_options);
+ crm_element_value_int(msg, PCMK__XA_LRMD_CALLOPT, &call_options);
cmd->call_opts = call_options;
- cmd->client_id = strdup(client->id);
-
- crm_element_value_int(msg, F_LRMD_CALLID, &cmd->call_id);
- crm_element_value_ms(rsc_xml, F_LRMD_RSC_INTERVAL, &cmd->interval_ms);
- crm_element_value_int(rsc_xml, F_LRMD_TIMEOUT, &cmd->timeout);
- crm_element_value_int(rsc_xml, F_LRMD_RSC_START_DELAY, &cmd->start_delay);
+ cmd->client_id = pcmk__str_copy(client->id);
+
+ crm_element_value_int(msg, PCMK__XA_LRMD_CALLID, &cmd->call_id);
+ crm_element_value_ms(rsc_xml, PCMK__XA_LRMD_RSC_INTERVAL,
+ &cmd->interval_ms);
+ crm_element_value_int(rsc_xml, PCMK__XA_LRMD_TIMEOUT, &cmd->timeout);
+ crm_element_value_int(rsc_xml, PCMK__XA_LRMD_RSC_START_DELAY,
+ &cmd->start_delay);
cmd->timeout_orig = cmd->timeout;
- cmd->origin = crm_element_value_copy(rsc_xml, F_LRMD_ORIGIN);
- cmd->action = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ACTION);
- cmd->userdata_str = crm_element_value_copy(rsc_xml, F_LRMD_RSC_USERDATA_STR);
- cmd->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID);
+ cmd->origin = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_ORIGIN);
+ cmd->action = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ACTION);
+ cmd->userdata_str = crm_element_value_copy(rsc_xml,
+ PCMK__XA_LRMD_RSC_USERDATA_STR);
+ cmd->rsc_id = crm_element_value_copy(rsc_xml, PCMK__XA_LRMD_RSC_ID);
cmd->params = xml2list(rsc_xml);
- if (pcmk__str_eq(g_hash_table_lookup(cmd->params, "CRM_meta_on_fail"), "block", pcmk__str_casei)) {
+ if (pcmk__str_eq(g_hash_table_lookup(cmd->params, "CRM_meta_on_fail"),
+ PCMK_VALUE_BLOCK, pcmk__str_casei)) {
crm_debug("Setting flag to leave pid group on timeout and "
"only kill action pid for " PCMK__OP_FMT,
cmd->rsc_id, cmd->action, cmd->interval_ms);
@@ -535,11 +539,11 @@ schedule_lrmd_cmd(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
static xmlNode *
create_lrmd_reply(const char *origin, int rc, int call_id)
{
- xmlNode *reply = create_xml_node(NULL, T_LRMD_REPLY);
+ xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_LRMD_REPLY);
- crm_xml_add(reply, F_LRMD_ORIGIN, origin);
- crm_xml_add_int(reply, F_LRMD_RC, rc);
- crm_xml_add_int(reply, F_LRMD_CALLID, call_id);
+ crm_xml_add(reply, PCMK__XA_LRMD_ORIGIN, origin);
+ crm_xml_add_int(reply, PCMK__XA_LRMD_RC, rc);
+ crm_xml_add_int(reply, PCMK__XA_LRMD_CALLID, call_id);
return reply;
}
@@ -614,41 +618,44 @@ send_cmd_complete_notify(lrmd_cmd_t * cmd)
cmd->last_notify_rc = cmd->result.exit_status;
cmd->last_notify_op_status = cmd->result.execution_status;
- notify = create_xml_node(NULL, T_LRMD_NOTIFY);
+ notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
- crm_xml_add(notify, F_LRMD_ORIGIN, __func__);
- crm_xml_add_int(notify, F_LRMD_TIMEOUT, cmd->timeout);
- crm_xml_add_ms(notify, F_LRMD_RSC_INTERVAL, cmd->interval_ms);
- crm_xml_add_int(notify, F_LRMD_RSC_START_DELAY, cmd->start_delay);
- crm_xml_add_int(notify, F_LRMD_EXEC_RC, cmd->result.exit_status);
- crm_xml_add_int(notify, F_LRMD_OP_STATUS, cmd->result.execution_status);
- crm_xml_add_int(notify, F_LRMD_CALLID, cmd->call_id);
- crm_xml_add_int(notify, F_LRMD_RSC_DELETED, cmd->rsc_deleted);
+ crm_xml_add(notify, PCMK__XA_LRMD_ORIGIN, __func__);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_TIMEOUT, cmd->timeout);
+ crm_xml_add_ms(notify, PCMK__XA_LRMD_RSC_INTERVAL, cmd->interval_ms);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_RSC_START_DELAY, cmd->start_delay);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_RC, cmd->result.exit_status);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_OP_STATUS,
+ cmd->result.execution_status);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_CALLID, cmd->call_id);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_RSC_DELETED, cmd->rsc_deleted);
- crm_xml_add_ll(notify, F_LRMD_RSC_RUN_TIME,
+ crm_xml_add_ll(notify, PCMK__XA_LRMD_RUN_TIME,
(long long) cmd->epoch_last_run);
- crm_xml_add_ll(notify, F_LRMD_RSC_RCCHANGE_TIME,
+ crm_xml_add_ll(notify, PCMK__XA_LRMD_RCCHANGE_TIME,
(long long) cmd->epoch_rcchange);
#ifdef PCMK__TIME_USE_CGT
- crm_xml_add_int(notify, F_LRMD_RSC_EXEC_TIME, exec_time);
- crm_xml_add_int(notify, F_LRMD_RSC_QUEUE_TIME, queue_time);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_EXEC_TIME, exec_time);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_QUEUE_TIME, queue_time);
#endif
- crm_xml_add(notify, F_LRMD_OPERATION, LRMD_OP_RSC_EXEC);
- crm_xml_add(notify, F_LRMD_RSC_ID, cmd->rsc_id);
+ crm_xml_add(notify, PCMK__XA_LRMD_OP, LRMD_OP_RSC_EXEC);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_ID, cmd->rsc_id);
if(cmd->real_action) {
- crm_xml_add(notify, F_LRMD_RSC_ACTION, cmd->real_action);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_ACTION, cmd->real_action);
} else {
- crm_xml_add(notify, F_LRMD_RSC_ACTION, cmd->action);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_ACTION, cmd->action);
}
- crm_xml_add(notify, F_LRMD_RSC_USERDATA_STR, cmd->userdata_str);
- crm_xml_add(notify, F_LRMD_RSC_EXIT_REASON, cmd->result.exit_reason);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_USERDATA_STR, cmd->userdata_str);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_EXIT_REASON, cmd->result.exit_reason);
if (cmd->result.action_stderr != NULL) {
- crm_xml_add(notify, F_LRMD_RSC_OUTPUT, cmd->result.action_stderr);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_OUTPUT,
+ cmd->result.action_stderr);
} else if (cmd->result.action_stdout != NULL) {
- crm_xml_add(notify, F_LRMD_RSC_OUTPUT, cmd->result.action_stdout);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_OUTPUT,
+ cmd->result.action_stdout);
}
if (cmd->params) {
@@ -656,7 +663,7 @@ send_cmd_complete_notify(lrmd_cmd_t * cmd)
char *value = NULL;
GHashTableIter iter;
- xmlNode *args = create_xml_node(notify, XML_TAG_ATTRS);
+ xmlNode *args = pcmk__xe_create(notify, PCMK__XE_ATTRIBUTES);
g_hash_table_iter_init(&iter, cmd->params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
@@ -684,18 +691,19 @@ send_generic_notify(int rc, xmlNode * request)
if (pcmk__ipc_client_count() != 0) {
int call_id = 0;
xmlNode *notify = NULL;
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
- const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
- const char *op = crm_element_value(request, F_LRMD_OPERATION);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
+ LOG_ERR);
+ const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
+ const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
- crm_element_value_int(request, F_LRMD_CALLID, &call_id);
+ crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &call_id);
- notify = create_xml_node(NULL, T_LRMD_NOTIFY);
- crm_xml_add(notify, F_LRMD_ORIGIN, __func__);
- crm_xml_add_int(notify, F_LRMD_RC, rc);
- crm_xml_add_int(notify, F_LRMD_CALLID, call_id);
- crm_xml_add(notify, F_LRMD_OPERATION, op);
- crm_xml_add(notify, F_LRMD_RSC_ID, rsc_id);
+ notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
+ crm_xml_add(notify, PCMK__XA_LRMD_ORIGIN, __func__);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_RC, rc);
+ crm_xml_add_int(notify, PCMK__XA_LRMD_CALLID, call_id);
+ crm_xml_add(notify, PCMK__XA_LRMD_OP, op);
+ crm_xml_add(notify, PCMK__XA_LRMD_RSC_ID, rsc_id);
pcmk__foreach_ipc_client(send_client_notify, notify);
@@ -778,9 +786,9 @@ notify_of_new_client(pcmk__client_t *new_client)
struct notify_new_client_data data;
data.new_client = new_client;
- data.notify = create_xml_node(NULL, T_LRMD_NOTIFY);
- crm_xml_add(data.notify, F_LRMD_ORIGIN, __func__);
- crm_xml_add(data.notify, F_LRMD_OPERATION, LRMD_OP_NEW_CLIENT);
+ data.notify = pcmk__xe_create(NULL, PCMK__XE_LRMD_NOTIFY);
+ crm_xml_add(data.notify, PCMK__XA_LRMD_ORIGIN, __func__);
+ crm_xml_add(data.notify, PCMK__XA_LRMD_OP, LRMD_OP_NEW_CLIENT);
pcmk__foreach_ipc_client(notify_one_client, &data);
free_xml(data.notify);
}
@@ -853,7 +861,7 @@ action_complete(svc_action_t * action)
*/
goagain = true;
cmd->real_action = cmd->action;
- cmd->action = strdup(PCMK_ACTION_MONITOR);
+ cmd->action = pcmk__str_copy(PCMK_ACTION_MONITOR);
} else if (cmd->real_action != NULL) {
// This is follow-up monitor to check whether start/stop completed
@@ -1479,23 +1487,33 @@ process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id,
{
int rc = pcmk_ok;
time_t now = time(NULL);
- const char *protocol_version = crm_element_value(request, F_LRMD_PROTOCOL_VERSION);
+ const char *protocol_version =
+ crm_element_value(request, PCMK__XA_LRMD_PROTOCOL_VERSION);
const char *start_state = pcmk__env_option(PCMK__ENV_NODE_START_STATE);
- if (compare_version(protocol_version, LRMD_MIN_PROTOCOL_VERSION) < 0) {
+ if (compare_version(protocol_version, LRMD_COMPATIBLE_PROTOCOL) < 0) {
crm_err("Cluster API version must be greater than or equal to %s, not %s",
- LRMD_MIN_PROTOCOL_VERSION, protocol_version);
+ LRMD_COMPATIBLE_PROTOCOL, protocol_version);
rc = -EPROTO;
}
- if (pcmk__xe_attr_is_true(request, F_LRMD_IS_IPC_PROVIDER)) {
+ if (pcmk__xe_attr_is_true(request, PCMK__XA_LRMD_IS_IPC_PROVIDER)) {
#ifdef PCMK__COMPILE_REMOTE
if ((client->remote != NULL)
&& pcmk_is_set(client->flags,
pcmk__client_tls_handshake_complete)) {
+ const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
// This is a remote connection from a cluster node's controller
ipc_proxy_add_provider(client);
+
+ /* If this was a register operation, also ask for new schema files but
+ * only if it's supported by the protocol version.
+ */
+ if (pcmk__str_eq(op, CRM_OP_REGISTER, pcmk__str_none) &&
+ LRMD_SUPPORTS_SCHEMA_XFER(protocol_version)) {
+ remoted_request_cib_schema_files();
+ }
} else {
rc = -EACCES;
}
@@ -1505,9 +1523,9 @@ process_lrmd_signon(pcmk__client_t *client, xmlNode *request, int call_id,
}
*reply = create_lrmd_reply(__func__, rc, call_id);
- crm_xml_add(*reply, F_LRMD_OPERATION, CRM_OP_REGISTER);
- crm_xml_add(*reply, F_LRMD_CLIENTID, client->id);
- crm_xml_add(*reply, F_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION);
+ crm_xml_add(*reply, PCMK__XA_LRMD_OP, CRM_OP_REGISTER);
+ crm_xml_add(*reply, PCMK__XA_LRMD_CLIENTID, client->id);
+ crm_xml_add(*reply, PCMK__XA_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION);
crm_xml_add_ll(*reply, PCMK__XA_UPTIME, now - start_time);
if (start_state) {
@@ -1542,8 +1560,9 @@ static xmlNode *
process_lrmd_get_rsc_info(xmlNode *request, int call_id)
{
int rc = pcmk_ok;
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
- const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
+ LOG_ERR);
+ const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
xmlNode *reply = NULL;
lrmd_rsc_t *rsc = NULL;
@@ -1559,10 +1578,10 @@ process_lrmd_get_rsc_info(xmlNode *request, int call_id)
reply = create_lrmd_reply(__func__, rc, call_id);
if (rsc) {
- crm_xml_add(reply, F_LRMD_RSC_ID, rsc->rsc_id);
- crm_xml_add(reply, F_LRMD_CLASS, rsc->class);
- crm_xml_add(reply, F_LRMD_PROVIDER, rsc->provider);
- crm_xml_add(reply, F_LRMD_TYPE, rsc->type);
+ crm_xml_add(reply, PCMK__XA_LRMD_RSC_ID, rsc->rsc_id);
+ crm_xml_add(reply, PCMK__XA_LRMD_CLASS, rsc->class);
+ crm_xml_add(reply, PCMK__XA_LRMD_PROVIDER, rsc->provider);
+ crm_xml_add(reply, PCMK__XA_LRMD_TYPE, rsc->type);
}
return reply;
}
@@ -1573,8 +1592,9 @@ process_lrmd_rsc_unregister(pcmk__client_t *client, uint32_t id,
{
int rc = pcmk_ok;
lrmd_rsc_t *rsc = NULL;
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
- const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
+ LOG_ERR);
+ const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
if (!rsc_id) {
return -ENODEV;
@@ -1604,8 +1624,9 @@ process_lrmd_rsc_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
{
lrmd_rsc_t *rsc = NULL;
lrmd_cmd_t *cmd = NULL;
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
- const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
+ LOG_ERR);
+ const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
int call_id;
if (!rsc_id) {
@@ -1727,12 +1748,13 @@ cancel_all_recurring(lrmd_rsc_t * rsc, const char *client_id)
static int
process_lrmd_rsc_cancel(pcmk__client_t *client, uint32_t id, xmlNode *request)
{
- xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR);
- const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID);
- const char *action = crm_element_value(rsc_xml, F_LRMD_RSC_ACTION);
+ xmlNode *rsc_xml = get_xpath_object("//" PCMK__XE_LRMD_RSC, request,
+ LOG_ERR);
+ const char *rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
+ const char *action = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ACTION);
guint interval_ms = 0;
- crm_element_value_ms(rsc_xml, F_LRMD_RSC_INTERVAL, &interval_ms);
+ crm_element_value_ms(rsc_xml, PCMK__XA_LRMD_RSC_INTERVAL, &interval_ms);
if (!rsc_id || !action) {
return -EINVAL;
@@ -1744,17 +1766,17 @@ process_lrmd_rsc_cancel(pcmk__client_t *client, uint32_t id, xmlNode *request)
static void
add_recurring_op_xml(xmlNode *reply, lrmd_rsc_t *rsc)
{
- xmlNode *rsc_xml = create_xml_node(reply, F_LRMD_RSC);
+ xmlNode *rsc_xml = pcmk__xe_create(reply, PCMK__XE_LRMD_RSC);
- crm_xml_add(rsc_xml, F_LRMD_RSC_ID, rsc->rsc_id);
+ crm_xml_add(rsc_xml, PCMK__XA_LRMD_RSC_ID, rsc->rsc_id);
for (GList *item = rsc->recurring_ops; item != NULL; item = item->next) {
lrmd_cmd_t *cmd = item->data;
- xmlNode *op_xml = create_xml_node(rsc_xml, T_LRMD_RSC_OP);
+ xmlNode *op_xml = pcmk__xe_create(rsc_xml, PCMK__XE_LRMD_RSC_OP);
- crm_xml_add(op_xml, F_LRMD_RSC_ACTION,
- (cmd->real_action? cmd->real_action : cmd->action));
- crm_xml_add_ms(op_xml, F_LRMD_RSC_INTERVAL, cmd->interval_ms);
- crm_xml_add_int(op_xml, F_LRMD_TIMEOUT, cmd->timeout_orig);
+ crm_xml_add(op_xml, PCMK__XA_LRMD_RSC_ACTION,
+ pcmk__s(cmd->real_action, cmd->action));
+ crm_xml_add_ms(op_xml, PCMK__XA_LRMD_RSC_INTERVAL, cmd->interval_ms);
+ crm_xml_add_int(op_xml, PCMK__XA_LRMD_TIMEOUT, cmd->timeout_orig);
}
}
@@ -1768,12 +1790,12 @@ process_lrmd_get_recurring(xmlNode *request, int call_id)
xmlNode *rsc_xml = NULL;
// Resource ID is optional
- rsc_xml = first_named_child(request, F_LRMD_CALLDATA);
+ rsc_xml = pcmk__xe_first_child(request, PCMK__XE_LRMD_CALLDATA, NULL, NULL);
if (rsc_xml) {
- rsc_xml = first_named_child(rsc_xml, F_LRMD_RSC);
+ rsc_xml = pcmk__xe_first_child(rsc_xml, PCMK__XE_LRMD_RSC, NULL, NULL);
}
if (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 resource ID is specified, resource must exist
@@ -1809,7 +1831,7 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request)
{
int rc = pcmk_ok;
int call_id = 0;
- const char *op = crm_element_value(request, F_LRMD_OPERATION);
+ const char *op = crm_element_value(request, PCMK__XA_LRMD_OP);
int do_reply = 0;
int do_notify = 0;
xmlNode *reply = NULL;
@@ -1821,7 +1843,7 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request)
bool allowed = pcmk_is_set(client->flags, pcmk__client_privileged);
crm_trace("Processing %s operation from %s", op, client->id);
- crm_element_value_int(request, F_LRMD_CALLID, &call_id);
+ crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &call_id);
if (pcmk__str_eq(op, CRM_OP_IPC_FWD, pcmk__str_none)) {
#ifdef PCMK__COMPILE_REMOTE
@@ -1882,10 +1904,16 @@ process_lrmd_message(pcmk__client_t *client, uint32_t id, xmlNode *request)
do_reply = 1;
} else if (pcmk__str_eq(op, LRMD_OP_CHECK, pcmk__str_none)) {
if (allowed) {
- xmlNode *data = get_message_xml(request, F_LRMD_CALLDATA);
+ xmlNode *wrapper = pcmk__xe_first_child(request,
+ PCMK__XE_LRMD_CALLDATA,
+ NULL, NULL);
+ xmlNode *data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
+ const char *timeout = NULL;
CRM_LOG_ASSERT(data != NULL);
- pcmk__valid_sbd_timeout(crm_element_value(data, F_LRMD_WATCHDOG));
+ timeout = crm_element_value(data, PCMK__XA_LRMD_WATCHDOG);
+ pcmk__valid_stonith_watchdog_timeout(timeout);
} else {
rc = -EACCES;
}
diff --git a/daemons/execd/pacemaker-execd.c b/daemons/execd/pacemaker-execd.c
index e7e30eb..926e278 100644
--- a/daemons/execd/pacemaker-execd.c
+++ b/daemons/execd/pacemaker-execd.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.
*
@@ -14,7 +14,7 @@
#include <sys/types.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/services.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc.h>
@@ -89,9 +89,11 @@ get_stonith_connection(void)
stonith_api_delete(stonith_api);
stonith_api = NULL;
} else {
- stonith_api->cmds->register_notification(stonith_api,
- T_STONITH_NOTIFY_DISCONNECT,
- stonith_connection_destroy_cb);
+ stonith_api_operations_t *cmds = stonith_api->cmds;
+
+ cmds->register_notification(stonith_api,
+ PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ stonith_connection_destroy_cb);
}
}
return stonith_api;
@@ -102,7 +104,7 @@ lrmd_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
crm_trace("Connection %p", c);
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -141,12 +143,13 @@ lrmd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
}
if (!client->name) {
- const char *value = crm_element_value(request, F_LRMD_CLIENTNAME);
+ const char *value = crm_element_value(request,
+ PCMK__XA_LRMD_CLIENTNAME);
if (value == NULL) {
client->name = pcmk__itoa(pcmk__client_pid(c));
} else {
- client->name = strdup(value);
+ client->name = pcmk__str_copy(value);
}
}
@@ -155,9 +158,9 @@ lrmd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
lrmd_call_id = 1;
}
- crm_xml_add(request, F_LRMD_CLIENTID, client->id);
- crm_xml_add(request, F_LRMD_CLIENTNAME, client->name);
- crm_xml_add_int(request, F_LRMD_CALLID, lrmd_call_id);
+ crm_xml_add(request, PCMK__XA_LRMD_CLIENTID, client->id);
+ crm_xml_add(request, PCMK__XA_LRMD_CLIENTNAME, client->name);
+ crm_xml_add_int(request, PCMK__XA_LRMD_CALLID, lrmd_call_id);
process_lrmd_message(client, id, request);
@@ -281,11 +284,7 @@ static gboolean
lrmd_exit(gpointer data)
{
crm_info("Terminating with %d clients", pcmk__ipc_client_count());
- if (stonith_api) {
- stonith_api->cmds->remove_notification(stonith_api, T_STONITH_NOTIFY_DISCONNECT);
- stonith_api->cmds->disconnect(stonith_api);
- stonith_api_delete(stonith_api);
- }
+ stonith_api_delete(stonith_api);
if (ipcs) {
mainloop_del_ipc_server(ipcs);
}
@@ -443,19 +442,23 @@ main(int argc, char **argv, char **envp)
GError *error = NULL;
GOptionGroup *output_group = NULL;
- pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
-#ifdef PCMK__COMPILE_REMOTE
- gchar **processed_args = pcmk__cmdline_preproc(argv, "lp");
-#else
- gchar **processed_args = pcmk__cmdline_preproc(argv, "l");
-#endif // PCMK__COMPILE_REMOTE
- GOptionContext *context = build_arg_context(args, &output_group);
+ pcmk__common_args_t *args = NULL;
+ gchar **processed_args = NULL;
+ GOptionContext *context = NULL;
#ifdef PCMK__COMPILE_REMOTE
// If necessary, create PID 1 now before any file descriptors are opened
remoted_spawn_pidone(argc, argv, envp);
#endif
+ args = pcmk__new_common_args(SUMMARY);
+#ifdef PCMK__COMPILE_REMOTE
+ processed_args = pcmk__cmdline_preproc(argv, "lp");
+#else
+ processed_args = pcmk__cmdline_preproc(argv, "l");
+#endif // PCMK__COMPILE_REMOTE
+ context = build_arg_context(args, &output_group);
+
crm_log_preinit(EXECD_NAME, argc, argv);
pcmk__register_formats(output_group, formats);
@@ -495,7 +498,7 @@ main(int argc, char **argv, char **envp)
// ocf_log() (in resource-agents) uses the capitalized env options below
option = pcmk__env_option(PCMK__ENV_LOGFACILITY);
- if (!pcmk__str_eq(option, PCMK__VALUE_NONE,
+ if (!pcmk__str_eq(option, PCMK_VALUE_NONE,
pcmk__str_casei|pcmk__str_null_matches)
&& !pcmk__str_eq(option, "/dev/null", pcmk__str_none)) {
@@ -503,7 +506,7 @@ main(int argc, char **argv, char **envp)
}
option = pcmk__env_option(PCMK__ENV_LOGFILE);
- if (!pcmk__str_eq(option, PCMK__VALUE_NONE,
+ if (!pcmk__str_eq(option, PCMK_VALUE_NONE,
pcmk__str_casei|pcmk__str_null_matches)) {
pcmk__set_env_option("LOGFILE", option, true);
diff --git a/daemons/execd/pacemaker-execd.h b/daemons/execd/pacemaker-execd.h
index 9c1d173..6fb8ef4 100644
--- a/daemons/execd/pacemaker-execd.h
+++ b/daemons/execd/pacemaker-execd.h
@@ -101,6 +101,7 @@ void ipc_proxy_forward_client(pcmk__client_t *client, xmlNode *xml);
pcmk__client_t *ipc_proxy_get_provider(void);
int ipc_proxy_shutdown_req(pcmk__client_t *ipc_proxy);
void remoted_spawn_pidone(int argc, char **argv, char **envp);
+void remoted_request_cib_schema_files(void);
#endif
int process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id,
diff --git a/daemons/execd/remoted_pidone.c b/daemons/execd/remoted_pidone.c
index 08271bf..0a6c251 100644
--- a/daemons/execd/remoted_pidone.c
+++ b/daemons/execd/remoted_pidone.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 the Pacemaker project contributors
+ * Copyright 2017-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -25,7 +25,7 @@ static pid_t main_pid = 0;
static void
sigdone(void)
{
- exit(CRM_EX_OK);
+ crm_exit(CRM_EX_OK);
}
static void
@@ -44,9 +44,9 @@ sigreap(void)
if (pid == main_pid) {
/* Exit when pacemaker-remote exits and use the same return code */
if (WIFEXITED(status)) {
- exit(WEXITSTATUS(status));
+ crm_exit(WEXITSTATUS(status));
}
- exit(CRM_EX_ERROR);
+ crm_exit(CRM_EX_ERROR);
}
} while (pid > 0);
}
@@ -203,7 +203,7 @@ remoted_spawn_pidone(int argc, char **argv, char **envp)
* from /etc/pacemaker/pcmk-init.env, which could be useful for testing or
* containers with a custom PID 1 script that launches pacemaker-remoted.
*/
- const char *pid1 = "default";
+ const char *pid1 = PCMK_VALUE_DEFAULT;
if (getpid() != 1) {
pid1 = pcmk__env_option(PCMK__ENV_REMOTE_PID1);
diff --git a/daemons/execd/remoted_proxy.c b/daemons/execd/remoted_proxy.c
index 62c8c3a..40dfdc6 100644
--- a/daemons/execd/remoted_proxy.c
+++ b/daemons/execd/remoted_proxy.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 the Pacemaker project contributors
+ * Copyright 2012-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,7 @@
#include "pacemaker-execd.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/ipc.h>
@@ -81,12 +81,12 @@ ipc_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid, const char *ipc
*/
client = pcmk__new_client(c, uid, gid);
if (client == NULL) {
- return -EREMOTEIO;
+ return -ENOMEM;
}
/* This ipc client is bound to a single ipc provider. If the
* provider goes away, this client is disconnected */
- client->userdata = strdup(ipc_proxy->id);
+ client->userdata = pcmk__str_copy(ipc_proxy->id);
client->name = crm_strdup_printf("proxy-%s-%d-%.8s", ipc_channel, client->pid, client->id);
/* Allow remote executor to distinguish between proxied local clients and
@@ -96,10 +96,10 @@ ipc_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid, const char *ipc
g_hash_table_insert(ipc_clients, client->id, client);
- msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
- crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_NEW);
- crm_xml_add(msg, F_LRMD_IPC_IPC_SERVER, ipc_channel);
- crm_xml_add(msg, F_LRMD_IPC_SESSION, client->id);
+ msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_NEW);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_SERVER, ipc_channel);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
lrmd_server_send_notify(ipc_proxy, msg);
free_xml(msg);
crm_debug("Accepted IPC proxy connection (session ID %s) "
@@ -117,7 +117,7 @@ crmd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
static int32_t
attrd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
- return ipc_proxy_accept(c, uid, gid, T_ATTRD);
+ return ipc_proxy_accept(c, uid, gid, PCMK__VALUE_ATTRD);
}
static int32_t
@@ -147,9 +147,13 @@ cib_proxy_accept_ro(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
void
ipc_proxy_forward_client(pcmk__client_t *ipc_proxy, xmlNode *xml)
{
- const char *session = crm_element_value(xml, F_LRMD_IPC_SESSION);
- const char *msg_type = crm_element_value(xml, F_LRMD_IPC_OP);
- xmlNode *msg = get_message_xml(xml, F_LRMD_IPC_MSG);
+ const char *session = crm_element_value(xml, PCMK__XA_LRMD_IPC_SESSION);
+ const char *msg_type = crm_element_value(xml, PCMK__XA_LRMD_IPC_OP);
+
+ xmlNode *wrapper = pcmk__xe_first_child(xml, PCMK__XE_LRMD_IPC_MSG, NULL,
+ NULL);
+ xmlNode *msg = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
pcmk__client_t *ipc_client;
int rc = pcmk_rc_ok;
@@ -169,9 +173,9 @@ ipc_proxy_forward_client(pcmk__client_t *ipc_proxy, xmlNode *xml)
ipc_client = pcmk__find_client_by_id(session);
if (ipc_client == NULL) {
- 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);
+ 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);
lrmd_server_send_notify(ipc_proxy, msg);
free_xml(msg);
return;
@@ -198,7 +202,7 @@ ipc_proxy_forward_client(pcmk__client_t *ipc_proxy, xmlNode *xml)
} else if (pcmk__str_eq(msg_type, LRMD_IPC_OP_RESPONSE, pcmk__str_casei)) {
int msg_id = 0;
- crm_element_value_int(xml, F_LRMD_IPC_MSG_ID, &msg_id);
+ crm_element_value_int(xml, PCMK__XA_LRMD_IPC_MSG_ID, &msg_id);
crm_trace("Sending response to %d - %s", ipc_client->request_id, ipc_client->id);
rc = pcmk__ipc_send_xml(ipc_client, msg_id, msg, FALSE);
@@ -225,6 +229,7 @@ ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
uint32_t flags = 0;
pcmk__client_t *client = pcmk__find_client(c);
pcmk__client_t *ipc_proxy = pcmk__find_client_by_id(client->userdata);
+ xmlNode *wrapper = NULL;
xmlNode *request = NULL;
xmlNode *msg = NULL;
@@ -263,18 +268,22 @@ ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
pcmk__set_ipc_flags(flags, pcmk__client_name(client), crm_ipc_proxied);
client->request_id = id;
- msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
- crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_REQUEST);
- crm_xml_add(msg, F_LRMD_IPC_SESSION, client->id);
- crm_xml_add(msg, F_LRMD_IPC_CLIENT, pcmk__client_name(client));
- crm_xml_add(msg, F_LRMD_IPC_USER, client->user);
- crm_xml_add_int(msg, F_LRMD_IPC_MSG_ID, id);
- crm_xml_add_int(msg, F_LRMD_IPC_MSG_FLAGS, flags);
- add_message_xml(msg, F_LRMD_IPC_MSG, request);
+ msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_REQUEST);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_CLIENT, pcmk__client_name(client));
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_USER, client->user);
+ crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_ID, id);
+ crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, flags);
+
+ wrapper = pcmk__xe_create(msg, PCMK__XE_LRMD_IPC_MSG);
+
+ pcmk__xml_copy(wrapper, request);
+
lrmd_server_send_notify(ipc_proxy, msg);
+
free_xml(request);
free_xml(msg);
-
return 0;
}
@@ -289,15 +298,15 @@ ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
int
ipc_proxy_shutdown_req(pcmk__client_t *ipc_proxy)
{
- xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
+ xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
int rc;
- crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_REQ);
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_REQ);
/* We don't really have a session, but the controller needs this attribute
* to recognize this as proxy communication.
*/
- crm_xml_add(msg, F_LRMD_IPC_SESSION, "0");
+ crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, "0");
rc = (lrmd_server_send_notify(ipc_proxy, msg) != pcmk_rc_ok)? -1 : 0;
free_xml(msg);
@@ -319,9 +328,9 @@ ipc_proxy_closed(qb_ipcs_connection_t * c)
crm_trace("Connection %p", c);
if (ipc_proxy) {
- 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, client->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, client->id);
lrmd_server_send_notify(ipc_proxy, msg);
free_xml(msg);
}
diff --git a/daemons/execd/remoted_schemas.c b/daemons/execd/remoted_schemas.c
new file mode 100644
index 0000000..f0ec068
--- /dev/null
+++ b/daemons/execd/remoted_schemas.c
@@ -0,0 +1,286 @@
+/*
+ * 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 <ftw.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <crm/cib.h>
+#include <crm/cib/cib_types.h>
+#include <crm/cib/internal.h>
+#include <crm/crm.h>
+#include <crm/common/mainloop.h>
+#include <crm/common/xml.h>
+
+#include "pacemaker-execd.h"
+
+static pid_t schema_fetch_pid = 0;
+
+static int
+rm_files(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb)
+{
+ /* Don't delete PCMK__REMOTE_SCHEMA_DIR . */
+ if (ftwb->level == 0) {
+ return 0;
+ }
+
+ if (remove(pathname) != 0) {
+ int rc = errno;
+ crm_err("Could not remove %s: %s", pathname, pcmk_rc_str(rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+clean_up_extra_schema_files(void)
+{
+ const char *remote_schema_dir = pcmk__remote_schema_dir();
+ struct stat sb;
+ int rc;
+
+ rc = stat(remote_schema_dir, &sb);
+
+ if (rc == -1) {
+ if (errno == ENOENT) {
+ /* If the directory doesn't exist, try to make it first. */
+ if (mkdir(remote_schema_dir, 0755) != 0) {
+ rc = errno;
+ crm_err("Could not create directory for schemas: %s",
+ pcmk_rc_str(rc));
+ }
+
+ } else {
+ rc = errno;
+ crm_err("Could not create directory for schemas: %s",
+ pcmk_rc_str(rc));
+ }
+
+ } else if (!S_ISDIR(sb.st_mode)) {
+ /* If something exists with the same name that's not a directory, that's
+ * an error.
+ */
+ crm_err("%s already exists but is not a directory", remote_schema_dir);
+
+ } else {
+ /* It's a directory - clear it out so we can download potentially new
+ * schema files.
+ */
+ rc = nftw(remote_schema_dir, rm_files, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
+
+ if (rc != 0) {
+ crm_err("Could not remove %s: %s", remote_schema_dir, pcmk_rc_str(rc));
+ }
+ }
+}
+
+static void
+write_extra_schema_file(xmlNode *xml, void *user_data)
+{
+ const char *remote_schema_dir = pcmk__remote_schema_dir();
+ const char *file = NULL;
+ char *path = NULL;
+ int rc;
+
+ file = crm_element_value(xml, PCMK_XA_PATH);
+ if (file == NULL) {
+ crm_warn("No destination path given in schema request");
+ return;
+ }
+
+ path = crm_strdup_printf("%s/%s", remote_schema_dir, file);
+
+ /* The schema is a CDATA node, which is a child of the <file> node. Traverse
+ * all children and look for the first CDATA child. There can't be more than
+ * one because we only have one file attribute on the parent.
+ */
+ for (xmlNode *child = xml->children; child != NULL; child = child->next) {
+ FILE *stream = NULL;
+
+ if (child->type != XML_CDATA_SECTION_NODE) {
+ continue;
+ }
+
+ stream = fopen(path, "w+");
+ if (stream == NULL) {
+ crm_warn("Could not write schema file %s: %s", path, strerror(errno));
+ } else {
+ rc = fprintf(stream, "%s", child->content);
+
+ if (rc < 0) {
+ crm_warn("Could not write schema file %s: %s", path, strerror(errno));
+ }
+
+ fclose(stream);
+ }
+
+ break;
+ }
+
+ free(path);
+}
+
+static void
+get_schema_files(void)
+{
+ int rc = pcmk_rc_ok;
+ cib_t *cib = NULL;
+ xmlNode *reply;
+
+ cib = cib_new();
+ if (cib == NULL) {
+ _exit(ENOTCONN);
+ }
+
+ rc = cib->cmds->signon(cib, crm_system_name, cib_query);
+ if (rc != pcmk_ok) {
+ crm_err("Could not connect to the CIB manager: %s", pcmk_strerror(rc));
+ _exit(pcmk_rc2exitc(rc));
+ }
+
+ rc = cib->cmds->fetch_schemas(cib, &reply, pcmk__highest_schema_name(),
+ cib_sync_call);
+ if (rc != pcmk_ok) {
+ crm_err("Could not get schema files: %s", pcmk_strerror(rc));
+ rc = pcmk_legacy2rc(rc);
+
+ } else if (reply->children != NULL) {
+ /* The returned document looks something like this:
+ * <cib_command>
+ * <cib_calldata>
+ * <schemas>
+ * <schema version="pacemaker-1.1">
+ * <file path="foo-1.1">
+ * </file>
+ * <file path="bar-1.1">
+ * </file>
+ * ...
+ * </schema>
+ * <schema version="pacemaker-1.2">
+ * </schema>
+ * ...
+ * </schemas>
+ * </cib_calldata>
+ * </cib_command>
+ *
+ * All the <schemas> and <schema> tags are really just there for organizing
+ * the XML a little better. What we really care about are the <file> nodes,
+ * and specifically the path attributes and the CDATA children (not shown)
+ * of each. We can use an xpath query to reach down and get all the <file>
+ * nodes at once.
+ *
+ * If we already have the latest schema version, or we asked for one later
+ * than what the cluster supports, we'll get back an empty <schemas> node,
+ * so all this will continue to work. It just won't do anything.
+ */
+ crm_foreach_xpath_result(reply, "//" PCMK_XA_FILE,
+ write_extra_schema_file, NULL);
+ }
+
+ free_xml(reply);
+ cib__clean_up_connection(&cib);
+ _exit(pcmk_rc2exitc(rc));
+}
+
+/* Load any additional schema files when the child is finished fetching and
+ * saving them to disk.
+ */
+static void
+get_schema_files_complete(mainloop_child_t *p, pid_t pid, int core, int signo, int exitcode)
+{
+ const char *errmsg = "Could not load additional schema files";
+
+ if ((signo == 0) && (exitcode == 0)) {
+ const char *remote_schema_dir = pcmk__remote_schema_dir();
+
+ /* Don't just crm_schema_init here because that will load the base
+ * schemas again too. Instead just load the things we fetched.
+ */
+ pcmk__load_schemas_from_dir(remote_schema_dir);
+ pcmk__sort_schemas();
+ crm_info("Fetching extra schema files completed successfully");
+
+ } else {
+ if (signo == 0) {
+ crm_err("%s: process %d exited %d", errmsg, (int) pid, exitcode);
+
+ } else {
+ crm_err("%s: process %d terminated with signal %d (%s)%s",
+ errmsg, (int) pid, signo, strsignal(signo),
+ (core? " and dumped core" : ""));
+ }
+
+ /* Clean up any incomplete schema data we might have been downloading when
+ * the process timed out or crashed. We don't need to do any extra cleanup
+ * because we never loaded the extra schemas, and we don't need to call
+ * crm_schema_init because that was called in remoted_request_cib_schema_files
+ * before this function.
+ */
+ clean_up_extra_schema_files();
+ }
+}
+
+void
+remoted_request_cib_schema_files(void)
+{
+ pid_t pid;
+ int rc;
+
+ /* If a previous schema-fetch process is still running when we're called
+ * again, it's hung. Attempt to kill it before cleaning up the extra
+ * directory.
+ */
+ if (schema_fetch_pid != 0) {
+ if (mainloop_child_kill(schema_fetch_pid) == FALSE) {
+ crm_warn("Unable to kill pre-existing schema-fetch process");
+ return;
+ }
+
+ schema_fetch_pid = 0;
+ }
+
+ /* Clean up any extra schema files we downloaded from a previous cluster
+ * connection. After the files are gone, we need to wipe them from
+ * known_schemas, but there's no opposite operation for add_schema().
+ *
+ * Instead, unload all the schemas. This means we'll also forget about all
+ * installed schemas as well, which means that pcmk__highest_schema_name()
+ * would fail. So we need to load the base schemas right now.
+ */
+ clean_up_extra_schema_files();
+ crm_schema_cleanup();
+ crm_schema_init();
+
+ crm_info("Fetching extra schema files from cluster");
+ pid = fork();
+
+ switch (pid) {
+ case -1: {
+ rc = errno;
+ crm_warn("Could not spawn process to get schema files: %s", pcmk_rc_str(rc));
+ break;
+ }
+
+ case 0:
+ /* child */
+ get_schema_files();
+ break;
+
+ default:
+ /* parent */
+ schema_fetch_pid = pid;
+ mainloop_child_add_with_flags(pid, 5 * 60 * 1000, "schema-fetch", NULL,
+ mainloop_leave_pid_group,
+ get_schema_files_complete);
+ break;
+ }
+}
diff --git a/daemons/execd/remoted_tls.c b/daemons/execd/remoted_tls.c
index 23a2dcf..e98991f 100644
--- a/daemons/execd/remoted_tls.c
+++ b/daemons/execd/remoted_tls.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.
*
@@ -13,10 +13,8 @@
#include <unistd.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
-#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/mainloop.h>
+#include <crm/common/xml.h>
#include <crm/common/remote_internal.h>
#include <crm/lrmd_internal.h>
@@ -111,14 +109,11 @@ lrmd_remote_client_msg(gpointer data)
request = pcmk__remote_message_xml(client->remote);
while (request) {
- crm_element_value_int(request, F_LRMD_REMOTE_MSG_ID, &id);
+ crm_element_value_int(request, PCMK__XA_LRMD_REMOTE_MSG_ID, &id);
crm_trace("Processing remote client request %d", id);
if (!client->name) {
- const char *value = crm_element_value(request, F_LRMD_CLIENTNAME);
-
- if (value) {
- client->name = strdup(value);
- }
+ client->name = crm_element_value_copy(request,
+ PCMK__XA_LRMD_CLIENTNAME);
}
lrmd_call_id++;
@@ -126,9 +121,9 @@ lrmd_remote_client_msg(gpointer data)
lrmd_call_id = 1;
}
- crm_xml_add(request, F_LRMD_CLIENTID, client->id);
- crm_xml_add(request, F_LRMD_CLIENTNAME, client->name);
- crm_xml_add_int(request, F_LRMD_CALLID, lrmd_call_id);
+ crm_xml_add(request, PCMK__XA_LRMD_CLIENTID, client->id);
+ crm_xml_add(request, PCMK__XA_LRMD_CLIENTNAME, client->name);
+ crm_xml_add_int(request, PCMK__XA_LRMD_CALLID, lrmd_call_id);
process_lrmd_message(client, id, request);
free_xml(request);
@@ -175,6 +170,7 @@ lrmd_remote_client_destroy(gpointer user_data)
gnutls_bye(*client->remote->tls_session, GNUTLS_SHUT_RDWR);
gnutls_deinit(*client->remote->tls_session);
gnutls_free(client->remote->tls_session);
+ client->remote->tls_session = NULL;
close(csock);
}
@@ -229,7 +225,7 @@ lrmd_remote_listen(gpointer data)
}
new_client = pcmk__new_unauth_client(NULL);
- new_client->remote = calloc(1, sizeof(pcmk__remote_t));
+ new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
pcmk__set_client_flags(new_client, pcmk__client_tls);
new_client->remote->tls_session = session;
diff --git a/daemons/fenced/cts-fence-helper.c b/daemons/fenced/cts-fence-helper.c
index 07bd500..edde8ca 100644
--- a/daemons/fenced/cts-fence-helper.c
+++ b/daemons/fenced/cts-fence-helper.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2023 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
@@ -20,7 +20,6 @@
#include <fcntl.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
@@ -164,8 +163,10 @@ passive_test(void)
stonith_api_delete(st);
crm_exit(CRM_EX_DISCONNECT);
}
- st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback);
- st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, st_callback);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ st_callback);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
+ st_callback);
st->cmds->register_notification(st, STONITH_OP_DEVICE_ADD, st_callback);
st->cmds->register_notification(st, STONITH_OP_DEVICE_DEL, st_callback);
st->cmds->register_callback(st, 0, 120, st_opt_timeout_updates, NULL, "st_global_callback",
@@ -325,8 +326,10 @@ sanity_tests(void)
stonith_api_delete(st);
crm_exit(CRM_EX_DISCONNECT);
}
- st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT, st_callback);
- st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, st_callback);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ st_callback);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
+ st_callback);
st->cmds->register_notification(st, STONITH_OP_DEVICE_ADD, st_callback);
st->cmds->register_notification(st, STONITH_OP_DEVICE_DEL, st_callback);
st->cmds->register_callback(st, 0, 120, st_opt_timeout_updates, NULL, "st_global_callback",
diff --git a/daemons/fenced/fenced_cib.c b/daemons/fenced/fenced_cib.c
index e11bf68..6bf0e6f 100644
--- a/daemons/fenced/fenced_cib.c
+++ b/daemons/fenced/fenced_cib.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.
*
@@ -15,7 +15,6 @@
#include <libxml/xpath.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/cluster/internal.h>
@@ -55,11 +54,12 @@ node_has_attr(const char *node, const char *name, const char *value)
*/
xpath = g_string_sized_new(256);
pcmk__g_strcat(xpath,
- "//" XML_CIB_TAG_NODES "/" XML_CIB_TAG_NODE
- "[@" XML_ATTR_UNAME "='", node, "']/" XML_TAG_ATTR_SETS
- "/" XML_CIB_TAG_NVPAIR
- "[@" XML_NVPAIR_ATTR_NAME "='", name, "' "
- "and @" XML_NVPAIR_ATTR_VALUE "='", value, "']", NULL);
+ "//" PCMK_XE_NODES "/" PCMK_XE_NODE
+ "[@" PCMK_XA_UNAME "='", node, "']"
+ "/" PCMK_XE_INSTANCE_ATTRIBUTES
+ "/" PCMK_XE_NVPAIR
+ "[@" PCMK_XA_NAME "='", name, "' "
+ "and @" PCMK_XA_VALUE "='", value, "']", NULL);
match = get_xpath_object((const char *) xpath->str, local_cib, LOG_NEVER);
@@ -76,7 +76,7 @@ add_topology_level(xmlNode *match)
CRM_CHECK(match != NULL, return);
fenced_register_level(match, &desc, &result);
- fenced_send_level_notification(STONITH_OP_LEVEL_ADD, &result, desc);
+ fenced_send_config_notification(STONITH_OP_LEVEL_ADD, &result, desc);
pcmk__reset_result(&result);
free(desc);
}
@@ -86,14 +86,14 @@ topology_remove_helper(const char *node, int level)
{
char *desc = NULL;
pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
- xmlNode *data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
+ xmlNode *data = pcmk__xe_create(NULL, PCMK_XE_FENCING_LEVEL);
- crm_xml_add(data, F_STONITH_ORIGIN, __func__);
- crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
- crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
+ crm_xml_add(data, PCMK__XA_ST_ORIGIN, __func__);
+ crm_xml_add_int(data, PCMK_XA_INDEX, level);
+ crm_xml_add(data, PCMK_XA_TARGET, node);
fenced_unregister_level(data, &desc, &result);
- fenced_send_level_notification(STONITH_OP_LEVEL_DEL, &result, desc);
+ fenced_send_config_notification(STONITH_OP_LEVEL_DEL, &result, desc);
pcmk__reset_result(&result);
free_xml(data);
free(desc);
@@ -108,7 +108,7 @@ remove_topology_level(xmlNode *match)
CRM_CHECK(match != NULL, return);
key = stonith_level_key(match, fenced_target_by_unknown);
- crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
+ crm_element_value_int(match, PCMK_XA_INDEX, &index);
topology_remove_helper(key, index);
free(key);
}
@@ -149,7 +149,7 @@ void
fencing_topology_init(void)
{
xmlXPathObjectPtr xpathObj = NULL;
- const char *xpath = "//" XML_TAG_FENCING_LEVEL;
+ const char *xpath = "//" PCMK_XE_FENCING_LEVEL;
crm_trace("Full topology refresh");
free_topology_list();
@@ -174,37 +174,41 @@ remove_cib_device(xmlXPathObjectPtr xpathObj)
CRM_LOG_ASSERT(match != NULL);
if(match != NULL) {
- standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
+ standard = crm_element_value(match, PCMK_XA_CLASS);
}
if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
continue;
}
- rsc_id = crm_element_value(match, XML_ATTR_ID);
+ rsc_id = crm_element_value(match, PCMK_XA_ID);
stonith_device_remove(rsc_id, true);
}
}
+#define XPATH_WATCHDOG_TIMEOUT "//" PCMK_XE_NVPAIR \
+ "[@" PCMK_XA_NAME "='" \
+ PCMK_OPT_STONITH_WATCHDOG_TIMEOUT "']"
+
static void
update_stonith_watchdog_timeout_ms(xmlNode *cib)
{
- long timeout_ms = 0;
+ long long timeout_ms = 0;
xmlNode *stonith_watchdog_xml = NULL;
const char *value = NULL;
- stonith_watchdog_xml = get_xpath_object("//nvpair[@name='stonith-watchdog-timeout']",
- cib, LOG_NEVER);
+ stonith_watchdog_xml = get_xpath_object(XPATH_WATCHDOG_TIMEOUT, cib,
+ LOG_NEVER);
if (stonith_watchdog_xml) {
- value = crm_element_value(stonith_watchdog_xml, XML_NVPAIR_ATTR_VALUE);
+ value = crm_element_value(stonith_watchdog_xml, PCMK_XA_VALUE);
}
if (value) {
timeout_ms = crm_get_msec(value);
}
if (timeout_ms < 0) {
- timeout_ms = pcmk__auto_watchdog_timeout();
+ timeout_ms = pcmk__auto_stonith_watchdog_timeout();
}
stonith_watchdog_timeout_ms = timeout_ms;
@@ -221,9 +225,9 @@ cib_devices_update(void)
stonith_device_t *device = NULL;
crm_info("Updating devices to version %s.%s.%s",
- crm_element_value(local_cib, XML_ATTR_GENERATION_ADMIN),
- crm_element_value(local_cib, XML_ATTR_GENERATION),
- crm_element_value(local_cib, XML_ATTR_NUMUPDATES));
+ crm_element_value(local_cib, PCMK_XA_ADMIN_EPOCH),
+ crm_element_value(local_cib, PCMK_XA_EPOCH),
+ crm_element_value(local_cib, PCMK_XA_NUM_UPDATES));
g_hash_table_iter_init(&iter, device_list);
while (g_hash_table_iter_next(&iter, NULL, (void **)&device)) {
@@ -256,7 +260,9 @@ update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
xmlXPathObjectPtr xpath_obj = NULL;
/* process new constraints */
- xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_CONS_TAG_RSC_LOCATION);
+ xpath_obj = xpath_search(msg,
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK_XE_RSC_LOCATION);
if (numXpathResults(xpath_obj) > 0) {
int max = numXpathResults(xpath_obj), lpc = 0;
@@ -273,14 +279,20 @@ update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
freeXpathObject(xpath_obj);
/* process deletions */
- xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_RESOURCE);
+ xpath_obj = xpath_search(msg,
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_REMOVED
+ "//" PCMK_XE_PRIMITIVE);
if (numXpathResults(xpath_obj) > 0) {
remove_cib_device(xpath_obj);
}
freeXpathObject(xpath_obj);
/* process additions */
- xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_RESOURCE);
+ xpath_obj = xpath_search(msg,
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK_XE_PRIMITIVE);
if (numXpathResults(xpath_obj) > 0) {
int max = numXpathResults(xpath_obj), lpc = 0;
@@ -289,8 +301,8 @@ update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
const char *standard = NULL;
xmlNode *match = getXpathResult(xpath_obj, lpc);
- rsc_id = crm_element_value(match, XML_ATTR_ID);
- standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
+ rsc_id = crm_element_value(match, PCMK_XA_ID);
+ standard = crm_element_value(match, PCMK_XA_CLASS);
if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
continue;
@@ -314,35 +326,39 @@ update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
{
xmlNode *change = NULL;
char *reason = NULL;
- bool needs_update = FALSE;
- xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
+ NULL, NULL);
+ xmlNode *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
+ for (change = pcmk__xe_first_child(patchset, NULL, NULL, NULL);
+ change != NULL; change = pcmk__xe_next(change)) {
- for (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);
+ const char *op = crm_element_value(change, PCMK_XA_OPERATION);
+ const char *xpath = crm_element_value(change, PCMK_XA_PATH);
const char *shortpath = NULL;
- if ((op == NULL) ||
- (strcmp(op, "move") == 0) ||
- strstr(xpath, "/"XML_CIB_TAG_STATUS)) {
+ if (pcmk__str_eq(op, PCMK_VALUE_MOVE, pcmk__str_null_matches)
+ || (strstr(xpath, "/" PCMK_XE_STATUS) != NULL)) {
continue;
- } else if (pcmk__str_eq(op, "delete", pcmk__str_casei) && strstr(xpath, "/"XML_CIB_TAG_RESOURCE)) {
+ }
+
+ if (pcmk__str_eq(op, PCMK_VALUE_DELETE, pcmk__str_none)
+ && (strstr(xpath, "/" PCMK_XE_PRIMITIVE) != NULL)) {
const char *rsc_id = NULL;
char *search = NULL;
char *mutable = NULL;
- if (strstr(xpath, XML_TAG_ATTR_SETS) ||
- strstr(xpath, XML_TAG_META_SETS)) {
- needs_update = TRUE;
- pcmk__str_update(&reason,
- "(meta) attribute deleted from resource");
+ if ((strstr(xpath, PCMK_XE_INSTANCE_ATTRIBUTES) != NULL)
+ || (strstr(xpath, PCMK_XE_META_ATTRIBUTES) != NULL)) {
+
+ reason = pcmk__str_copy("(meta) attribute deleted from "
+ "resource");
break;
}
- pcmk__str_update(&mutable, xpath);
- rsc_id = strstr(mutable, "primitive[@" XML_ATTR_ID "=\'");
+ mutable = pcmk__str_copy(xpath);
+ rsc_id = strstr(mutable, PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "=\'");
if (rsc_id != NULL) {
- rsc_id += strlen("primitive[@" XML_ATTR_ID "=\'");
+ rsc_id += strlen(PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "=\'");
search = strchr(rsc_id, '\'');
}
if (search != NULL) {
@@ -355,30 +371,31 @@ update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
}
free(mutable);
- } else if (strstr(xpath, "/"XML_CIB_TAG_RESOURCES) ||
- strstr(xpath, "/"XML_CIB_TAG_CONSTRAINTS) ||
- strstr(xpath, "/"XML_CIB_TAG_RSCCONFIG)) {
+ } else if (strstr(xpath, "/" PCMK_XE_RESOURCES)
+ || strstr(xpath, "/" PCMK_XE_CONSTRAINTS)
+ || strstr(xpath, "/" PCMK_XE_RSC_DEFAULTS)) {
shortpath = strrchr(xpath, '/'); CRM_ASSERT(shortpath);
reason = crm_strdup_printf("%s %s", op, shortpath+1);
- needs_update = TRUE;
break;
}
}
- if(needs_update) {
+ if (reason != NULL) {
crm_info("Updating device list from CIB: %s", reason);
cib_devices_update();
+ free(reason);
} else {
crm_trace("No updates for device list found in CIB");
}
- free(reason);
}
static void
update_cib_stonith_devices(const char *event, xmlNode * msg)
{
int format = 1;
- xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
+ NULL, NULL);
+ xmlNode *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
CRM_ASSERT(patchset);
crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
@@ -465,17 +482,19 @@ remove_fencing_topology(xmlXPathObjectPtr xpathObj)
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_LOG_ASSERT(match != NULL);
- if (match && crm_element_value(match, XML_DIFF_MARKER)) {
+ if (match && crm_element_value(match, PCMK__XA_CRM_DIFF_MARKER)) {
/* Deletion */
int index = 0;
char *target = stonith_level_key(match, fenced_target_by_unknown);
- crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
+ crm_element_value_int(match, PCMK_XA_INDEX, &index);
if (target == NULL) {
- crm_err("Invalid fencing target in element %s", ID(match));
+ crm_err("Invalid fencing target in element %s",
+ pcmk__xe_id(match));
} else if (index <= 0) {
- crm_err("Invalid level for %s in element %s", target, ID(match));
+ crm_err("Invalid level for %s in element %s",
+ target, pcmk__xe_id(match));
} else {
topology_remove_helper(target, index);
@@ -491,21 +510,27 @@ update_fencing_topology(const char *event, xmlNode * msg)
int format = 1;
const char *xpath;
xmlXPathObjectPtr xpathObj = NULL;
- xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
+ NULL, NULL);
+ xmlNode *patchset = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
CRM_ASSERT(patchset);
crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
if(format == 1) {
/* Process deletions (only) */
- xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_FENCING_LEVEL;
+ xpath = "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_REMOVED
+ "//" PCMK_XE_FENCING_LEVEL;
xpathObj = xpath_search(msg, xpath);
remove_fencing_topology(xpathObj);
freeXpathObject(xpathObj);
/* Process additions and changes */
- xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_TAG_FENCING_LEVEL;
+ xpath = "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK_XE_FENCING_LEVEL;
xpathObj = xpath_search(msg, xpath);
register_fencing_topology(xpathObj);
@@ -518,33 +543,36 @@ update_fencing_topology(const char *event, xmlNode * msg)
xml_patch_versions(patchset, add, del);
- for (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 (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;
- } else if(strstr(xpath, "/" XML_TAG_FENCING_LEVEL) != NULL) {
+ } else if(strstr(xpath, "/" PCMK_XE_FENCING_LEVEL) != NULL) {
/* Change to a specific entry */
crm_trace("Handling %s operation %d.%d.%d for %s", op, add[0], add[1], add[2], xpath);
- if(strcmp(op, "move") == 0) {
+ if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
continue;
- } else if(strcmp(op, "create") == 0) {
+ } else if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
add_topology_level(change->children);
- } else if(strcmp(op, "modify") == 0) {
- xmlNode *match = first_named_child(change, XML_DIFF_RESULT);
+ } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
+ xmlNode *match = pcmk__xe_first_child(change,
+ PCMK_XE_CHANGE_RESULT,
+ NULL, NULL);
if(match) {
remove_topology_level(match->children);
add_topology_level(match->children);
}
- } else if(strcmp(op, "delete") == 0) {
+ } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
/* Nuclear option, all we have is the path and an id... not enough to remove a specific entry */
crm_info("Re-initializing fencing topology after %s operation %d.%d.%d for %s",
op, add[0], add[1], add[2], xpath);
@@ -552,20 +580,23 @@ update_fencing_topology(const char *event, xmlNode * msg)
return;
}
- } else if (strstr(xpath, "/" XML_TAG_FENCING_TOPOLOGY) != NULL) {
+ } else if (strstr(xpath, "/" PCMK_XE_FENCING_TOPOLOGY) != NULL) {
/* Change to the topology in general */
crm_info("Re-initializing fencing topology after top-level %s operation %d.%d.%d for %s",
op, add[0], add[1], add[2], xpath);
fencing_topology_init();
return;
- } else if (strstr(xpath, "/" XML_CIB_TAG_CONFIGURATION)) {
+ } else if (strstr(xpath, "/" PCMK_XE_CONFIGURATION)) {
/* Changes to the whole config section, possibly including the topology as a whild */
- if(first_named_child(change, XML_TAG_FENCING_TOPOLOGY) == NULL) {
+ if (pcmk__xe_first_child(change, PCMK_XE_FENCING_TOPOLOGY, NULL,
+ NULL) == NULL) {
crm_trace("Nothing for us in %s operation %d.%d.%d for %s.",
op, add[0], add[1], add[2], xpath);
- } else if(strcmp(op, "delete") == 0 || strcmp(op, "create") == 0) {
+ } else if (pcmk__str_any_of(op,
+ PCMK_VALUE_DELETE,
+ PCMK_VALUE_CREATE, NULL)) {
crm_info("Re-initializing fencing topology after top-level %s operation %d.%d.%d for %s.",
op, add[0], add[1], add[2], xpath);
fencing_topology_init();
@@ -586,7 +617,7 @@ update_fencing_topology(const char *event, xmlNode * msg)
static void
update_cib_cache_cb(const char *event, xmlNode * msg)
{
- long timeout_ms_saved = stonith_watchdog_timeout_ms;
+ long long timeout_ms_saved = stonith_watchdog_timeout_ms;
bool need_full_refresh = false;
if(!have_cib_devices) {
@@ -603,14 +634,18 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
*/
if (local_cib != NULL) {
int rc = pcmk_ok;
+ xmlNode *wrapper = NULL;
xmlNode *patchset = NULL;
- crm_element_value_int(msg, F_CIB_RC, &rc);
+ crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
if (rc != pcmk_ok) {
return;
}
- 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);
+
rc = xml_apply_patchset(local_cib, patchset, TRUE);
switch (rc) {
case pcmk_ok:
@@ -660,7 +695,7 @@ init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *us
{
crm_info("Updating device list from CIB");
have_cib_devices = TRUE;
- local_cib = copy_xml(output);
+ local_cib = pcmk__xml_copy(NULL, output);
pcmk__refresh_node_caches_from_cib(local_cib);
update_stonith_watchdog_timeout_ms(local_cib);
@@ -693,7 +728,7 @@ void
fenced_cib_cleanup(void)
{
if (cib_api != NULL) {
- cib_api->cmds->del_notify_callback(cib_api, T_CIB_DIFF_NOTIFY,
+ cib_api->cmds->del_notify_callback(cib_api, PCMK__VALUE_CIB_DIFF_NOTIFY,
update_cib_cache_cb);
cib__clean_up_connection(&cib_api);
}
@@ -719,16 +754,20 @@ setup_cib(void)
if (rc != pcmk_ok) {
crm_err("Could not connect to the CIB manager: %s (%d)", pcmk_strerror(rc), rc);
+ return;
+ }
- } else if (pcmk_ok !=
- cib_api->cmds->add_notify_callback(cib_api, T_CIB_DIFF_NOTIFY, update_cib_cache_cb)) {
+ rc = cib_api->cmds->add_notify_callback(cib_api,
+ PCMK__VALUE_CIB_DIFF_NOTIFY,
+ update_cib_cache_cb);
+ if (rc != pcmk_ok) {
crm_err("Could not set CIB notification callback");
-
- } else {
- rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_scope_local);
- cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL, "init_cib_cache_cb",
- init_cib_cache_cb);
- cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
- crm_info("Watching for fencing topology changes");
+ return;
}
+
+ rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_scope_local);
+ cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL,
+ "init_cib_cache_cb", init_cib_cache_cb);
+ cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
+ crm_info("Watching for fencing topology changes");
}
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
index 7a62ed6..223a701 100644
--- a/daemons/fenced/fenced_commands.c
+++ b/daemons/fenced/fenced_commands.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2023 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -23,7 +23,6 @@
#include <ctype.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
@@ -129,7 +128,7 @@ static int
get_action_delay_max(const stonith_device_t *device, const char *action)
{
const char *value = NULL;
- int delay_max = 0;
+ guint delay_max = 0U;
if (!pcmk__is_fencing_action(action)) {
return 0;
@@ -137,10 +136,11 @@ get_action_delay_max(const stonith_device_t *device, const char *action)
value = g_hash_table_lookup(device->params, PCMK_STONITH_DELAY_MAX);
if (value) {
- delay_max = crm_parse_interval_spec(value) / 1000;
+ pcmk_parse_interval_spec(value, &delay_max);
+ delay_max /= 1000;
}
- return delay_max;
+ return (int) delay_max;
}
static int
@@ -148,7 +148,7 @@ get_action_delay_base(const stonith_device_t *device, const char *action,
const char *target)
{
char *hash_value = NULL;
- int delay_base = 0;
+ guint delay_base = 0U;
if (!pcmk__is_fencing_action(action)) {
return 0;
@@ -157,11 +157,9 @@ get_action_delay_base(const stonith_device_t *device, const char *action,
hash_value = g_hash_table_lookup(device->params, PCMK_STONITH_DELAY_BASE);
if (hash_value) {
- char *value = strdup(hash_value);
+ char *value = pcmk__str_copy(hash_value);
char *valptr = value;
- CRM_ASSERT(value != NULL);
-
if (target != NULL) {
for (char *val = strtok(value, "; \t"); val != NULL; val = strtok(NULL, "; \t")) {
char *mapval = strchr(val, ':');
@@ -181,13 +179,14 @@ get_action_delay_base(const stonith_device_t *device, const char *action,
}
if (strchr(value, ':') == 0) {
- delay_base = crm_parse_interval_spec(value) / 1000;
+ pcmk_parse_interval_spec(value, &delay_base);
+ delay_base /= 1000;
}
free(valptr);
}
- return delay_base;
+ return (int) delay_base;
}
/*!
@@ -232,7 +231,8 @@ get_action_timeout(const stonith_device_t *device, const char *action,
snprintf(buffer, sizeof(buffer), "pcmk_%s_timeout", action);
value = g_hash_table_lookup(device->params, buffer);
if (value) {
- return atoi(value);
+ long long timeout_ms = crm_get_msec(value);
+ return (int) QB_MIN(timeout_ms / 1000, INT_MAX);
}
}
return default_timeout;
@@ -345,34 +345,33 @@ create_async_command(xmlNode *msg)
return NULL;
}
- op = get_xpath_object("//@" F_STONITH_ACTION, msg, LOG_ERR);
+ op = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, msg, LOG_ERR);
if (op == NULL) {
return NULL;
}
- cmd = calloc(1, sizeof(async_command_t));
- CRM_ASSERT(cmd != NULL);
+ cmd = pcmk__assert_alloc(1, sizeof(async_command_t));
// All messages must include these
- cmd->action = crm_element_value_copy(op, F_STONITH_ACTION);
- cmd->op = crm_element_value_copy(msg, F_STONITH_OPERATION);
- cmd->client = crm_element_value_copy(msg, F_STONITH_CLIENTID);
+ cmd->action = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ACTION);
+ cmd->op = crm_element_value_copy(msg, PCMK__XA_ST_OP);
+ cmd->client = crm_element_value_copy(msg, PCMK__XA_ST_CLIENTID);
if ((cmd->action == NULL) || (cmd->op == NULL) || (cmd->client == NULL)) {
free_async_command(cmd);
return NULL;
}
- crm_element_value_int(msg, F_STONITH_CALLID, &(cmd->id));
- crm_element_value_int(msg, F_STONITH_CALLOPTS, &(cmd->options));
- crm_element_value_int(msg, F_STONITH_DELAY, &(cmd->start_delay));
- crm_element_value_int(msg, F_STONITH_TIMEOUT, &(cmd->default_timeout));
+ crm_element_value_int(msg, PCMK__XA_ST_CALLID, &(cmd->id));
+ crm_element_value_int(msg, PCMK__XA_ST_CALLOPT, &(cmd->options));
+ crm_element_value_int(msg, PCMK__XA_ST_DELAY, &(cmd->start_delay));
+ crm_element_value_int(msg, PCMK__XA_ST_TIMEOUT, &(cmd->default_timeout));
cmd->timeout = cmd->default_timeout;
- cmd->origin = crm_element_value_copy(msg, F_ORIG);
- cmd->remote_op_id = crm_element_value_copy(msg, F_STONITH_REMOTE_OP_ID);
- cmd->client_name = crm_element_value_copy(msg, F_STONITH_CLIENTNAME);
- cmd->target = crm_element_value_copy(op, F_STONITH_TARGET);
- cmd->device = crm_element_value_copy(op, F_STONITH_DEVICE);
+ cmd->origin = crm_element_value_copy(msg, PCMK__XA_SRC);
+ cmd->remote_op_id = crm_element_value_copy(msg, PCMK__XA_ST_REMOTE_OP);
+ cmd->client_name = crm_element_value_copy(msg, PCMK__XA_ST_CLIENTNAME);
+ cmd->target = crm_element_value_copy(op, PCMK__XA_ST_TARGET);
+ cmd->device = crm_element_value_copy(op, PCMK__XA_ST_DEVICE_ID);
cmd->done_cb = st_child_done;
@@ -645,12 +644,13 @@ schedule_stonith_command(async_command_t * cmd, stonith_device_t * device)
}
if (device->include_nodeid && (cmd->target != NULL)) {
- crm_node_t *node = crm_get_peer(0, cmd->target);
+ crm_node_t *node = pcmk__get_node(0, cmd->target, NULL,
+ pcmk__node_search_cluster_member);
cmd->target_nodeid = node->id;
}
- cmd->device = strdup(device->id);
+ cmd->device = pcmk__str_copy(device->id);
cmd->timeout = get_action_timeout(device, cmd->action, cmd->default_timeout);
if (cmd->remote_op_id) {
@@ -785,7 +785,7 @@ build_port_aliases(const char *hostmap, GList ** targets)
case ':':
if (lpc > last) {
free(name);
- name = calloc(1, 1 + lpc - last);
+ name = pcmk__assert_alloc(1, 1 + lpc - last);
memcpy(name, hostmap + last, lpc - last);
}
last = lpc + 1;
@@ -801,7 +801,7 @@ build_port_aliases(const char *hostmap, GList ** targets)
char *value = NULL;
int k = 0;
- value = calloc(1, 1 + lpc - last);
+ value = pcmk__assert_alloc(1, 1 + lpc - last);
memcpy(value, hostmap + last, lpc - last);
for (int i = 0; value[i] != '\0'; i++) {
@@ -814,7 +814,7 @@ build_port_aliases(const char *hostmap, GList ** targets)
crm_debug("Adding alias '%s'='%s'", name, value);
g_hash_table_replace(aliases, name, value);
if (targets) {
- *targets = g_list_append(*targets, strdup(value));
+ *targets = g_list_append(*targets, pcmk__str_copy(value));
}
value = NULL;
name = NULL;
@@ -888,10 +888,10 @@ get_agent_metadata(const char *agent, xmlNode ** metadata)
crm_err("Could not retrieve metadata for fencing agent %s", agent);
return EAGAIN;
}
- g_hash_table_replace(metadata_cache, strdup(agent), buffer);
+ g_hash_table_replace(metadata_cache, pcmk__str_copy(agent), buffer);
}
- *metadata = string2xml(buffer);
+ *metadata = pcmk__xml_parse(buffer);
return pcmk_rc_ok;
}
@@ -908,7 +908,8 @@ is_nodeid_required(xmlNode * xml)
return FALSE;
}
- xpath = xpath_search(xml, "//parameter[@name='nodeid']");
+ xpath = xpath_search(xml,
+ "//" PCMK_XE_PARAMETER "[@" PCMK_XA_NAME "='nodeid']");
if (numXpathResults(xpath) <= 0) {
freeXpathObject(xpath);
return FALSE;
@@ -944,7 +945,7 @@ read_action_metadata(stonith_device_t *device)
CRM_LOG_ASSERT(match != NULL);
if(match == NULL) { continue; };
- action = crm_element_value(match, "name");
+ action = crm_element_value(match, PCMK_XA_NAME);
if (pcmk__str_eq(action, PCMK_ACTION_LIST, pcmk__str_none)) {
stonith__set_device_flags(device->flags, device->id,
@@ -956,16 +957,23 @@ read_action_metadata(stonith_device_t *device)
stonith__set_device_flags(device->flags, device->id,
st_device_supports_reboot);
} else if (pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none)) {
- /* "automatic" means the cluster will unfence node when it joins */
- /* "required" is a deprecated synonym for "automatic" */
- if (pcmk__xe_attr_is_true(match, "automatic") || pcmk__xe_attr_is_true(match, "required")) {
+ /* PCMK_XA_AUTOMATIC means the cluster will unfence a node when it
+ * joins.
+ *
+ * @COMPAT PCMK__XA_REQUIRED is a deprecated synonym for
+ * PCMK_XA_AUTOMATIC.
+ */
+ if (pcmk__xe_attr_is_true(match, PCMK_XA_AUTOMATIC)
+ || pcmk__xe_attr_is_true(match, PCMK__XA_REQUIRED)) {
device->automatic_unfencing = TRUE;
}
stonith__set_device_flags(device->flags, device->id,
st_device_supports_on);
}
- if ((action != NULL) && pcmk__xe_attr_is_true(match, "on_target")) {
+ if ((action != NULL)
+ && pcmk__xe_attr_is_true(match, PCMK_XA_ON_TARGET)) {
+
pcmk__add_word(&(device->on_target_actions), 64, action);
}
}
@@ -993,7 +1001,7 @@ map_action(GHashTable *params, const char *action, const char *value)
} else {
crm_warn("Mapping %s='%s' to %s='%s'",
STONITH_ATTR_ACTION_OP, value, key, value);
- g_hash_table_insert(params, key, strdup(value));
+ g_hash_table_insert(params, key, pcmk__str_copy(value));
}
}
@@ -1023,7 +1031,8 @@ xml2device_params(const char *name, const xmlNode *dev)
crm_warn("Ignoring empty '%s' parameter", STONITH_ATTR_ACTION_OP);
} else if (strcmp(value, PCMK_ACTION_REBOOT) == 0) {
- crm_warn("Ignoring %s='reboot' (see stonith-action cluster property instead)",
+ crm_warn("Ignoring %s='reboot' (see " PCMK_OPT_STONITH_ACTION
+ " cluster property instead)",
STONITH_ATTR_ACTION_OP);
} else if (strcmp(value, PCMK_ACTION_OFF) == 0) {
@@ -1050,15 +1059,15 @@ target_list_type(stonith_device_t * dev)
if (check_type == NULL) {
if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_LIST)) {
- check_type = "static-list";
+ check_type = PCMK_VALUE_STATIC_LIST;
} else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)) {
- check_type = "static-list";
+ check_type = PCMK_VALUE_STATIC_LIST;
} else if (pcmk_is_set(dev->flags, st_device_supports_list)) {
- check_type = "dynamic-list";
+ check_type = PCMK_VALUE_DYNAMIC_LIST;
} else if (pcmk_is_set(dev->flags, st_device_supports_status)) {
- check_type = "status";
+ check_type = PCMK_VALUE_STATUS;
} else {
- check_type = PCMK__VALUE_NONE;
+ check_type = PCMK_VALUE_NONE;
}
}
@@ -1070,17 +1079,15 @@ build_device_from_xml(xmlNode *dev)
{
const char *value;
stonith_device_t *device = NULL;
- char *agent = crm_element_value_copy(dev, "agent");
+ char *agent = crm_element_value_copy(dev, PCMK_XA_AGENT);
CRM_CHECK(agent != NULL, return device);
- device = calloc(1, sizeof(stonith_device_t));
-
- CRM_CHECK(device != NULL, {free(agent); return device;});
+ device = pcmk__assert_alloc(1, sizeof(stonith_device_t));
- device->id = crm_element_value_copy(dev, XML_ATTR_ID);
+ device->id = crm_element_value_copy(dev, PCMK_XA_ID);
device->agent = agent;
- device->namespace = crm_element_value_copy(dev, "namespace");
+ device->namespace = crm_element_value_copy(dev, PCMK__XA_NAMESPACE);
device->params = xml2device_params(device->id, dev);
value = g_hash_table_lookup(device->params, PCMK_STONITH_HOST_LIST);
@@ -1092,8 +1099,10 @@ build_device_from_xml(xmlNode *dev)
device->aliases = build_port_aliases(value, &(device->targets));
value = target_list_type(device);
- if (!pcmk__str_eq(value, "static-list", pcmk__str_casei) && device->targets) {
- /* Other than "static-list", dev-> targets is unnecessary. */
+ if (!pcmk__str_eq(value, PCMK_VALUE_STATIC_LIST, pcmk__str_casei)
+ && (device->targets != NULL)) {
+
+ // device->targets is necessary only with PCMK_VALUE_STATIC_LIST
g_list_free_full(device->targets, free);
device->targets = NULL;
}
@@ -1125,8 +1134,8 @@ build_device_from_xml(xmlNode *dev)
device->include_nodeid = is_nodeid_required(device->agent_metadata);
}
- value = crm_element_value(dev, "rsc_provides");
- if (pcmk__str_eq(value, PCMK__VALUE_UNFENCING, pcmk__str_casei)) {
+ value = crm_element_value(dev, PCMK__XA_RSC_PROVIDES);
+ if (pcmk__str_eq(value, PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
device->automatic_unfencing = TRUE;
}
@@ -1159,17 +1168,17 @@ schedule_internal_command(const char *origin,
{
async_command_t *cmd = NULL;
- cmd = calloc(1, sizeof(async_command_t));
+ cmd = pcmk__assert_alloc(1, sizeof(async_command_t));
cmd->id = -1;
cmd->default_timeout = timeout ? timeout : 60;
cmd->timeout = cmd->default_timeout;
- cmd->action = strdup(action);
- pcmk__str_update(&cmd->target, target);
- cmd->device = strdup(device->id);
- cmd->origin = strdup(origin);
- cmd->client = strdup(crm_system_name);
- cmd->client_name = strdup(crm_system_name);
+ cmd->action = pcmk__str_copy(action);
+ cmd->target = pcmk__str_copy(target);
+ cmd->device = pcmk__str_copy(device->id);
+ cmd->origin = pcmk__str_copy(origin);
+ cmd->client = pcmk__str_copy(crm_system_name);
+ cmd->client_name = pcmk__str_copy(crm_system_name);
cmd->internal_user_data = internal_user_data;
cmd->done_cb = done_cb; /* cmd, not internal_user_data, is passed to 'done_cb' as the userdata */
@@ -1292,13 +1301,13 @@ dynamic_list_search_cb(int pid, const pcmk__action_result_t *result,
((result->exit_reason == NULL)? "" : ")"));
}
- /* Fall back to pcmk_host_check="status" if the user didn't explicitly
- * specify "dynamic-list".
+ /* Fall back to pcmk_host_check=PCMK_VALUE_STATUS if the user didn't
+ * explicitly specify PCMK_VALUE_DYNAMIC_LIST
*/
if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK) == NULL) {
crm_notice("Switching to pcmk_host_check='status' for %s", dev->id);
- g_hash_table_replace(dev->params, strdup(PCMK_STONITH_HOST_CHECK),
- strdup("status"));
+ pcmk__insert_dup(dev->params, PCMK_STONITH_HOST_CHECK,
+ PCMK_VALUE_STATUS);
}
}
@@ -1330,7 +1339,7 @@ device_params_diff(GHashTable *first, GHashTable *second) {
if(strstr(key, "CRM_meta") == key) {
continue;
- } else if(strcmp(key, "crm_feature_set") == 0) {
+ } else if (strcmp(key, PCMK_XA_CRM_FEATURE_SET) == 0) {
continue;
} else {
char *other_value = g_hash_table_lookup(second, key);
@@ -1389,7 +1398,7 @@ stonith_device_register(xmlNode *dev, gboolean from_cib)
STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) do {
if (stonith_watchdog_timeout_ms <= 0) {
crm_err("Ignoring watchdog fence device without "
- "stonith-watchdog-timeout set.");
+ PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " set.");
rv = -ENODEV;
/* fall through to cleanup & return */
} else if (!pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
@@ -1419,9 +1428,8 @@ stonith_device_register(xmlNode *dev, gboolean from_cib)
if (node_does_watchdog_fencing(stonith_our_uname)) {
g_list_free_full(device->targets, free);
device->targets = stonith__parse_targets(stonith_our_uname);
- g_hash_table_replace(device->params,
- strdup(PCMK_STONITH_HOST_LIST),
- strdup(stonith_our_uname));
+ pcmk__insert_dup(device->params,
+ PCMK_STONITH_HOST_LIST, stonith_our_uname);
/* proceed as with any other stonith-device */
break;
}
@@ -1578,18 +1586,18 @@ stonith_level_key(const xmlNode *level, enum fenced_target_by mode)
}
switch (mode) {
case fenced_target_by_name:
- return crm_element_value_copy(level, XML_ATTR_STONITH_TARGET);
+ return crm_element_value_copy(level, PCMK_XA_TARGET);
case fenced_target_by_pattern:
- return crm_element_value_copy(level, XML_ATTR_STONITH_TARGET_PATTERN);
+ return crm_element_value_copy(level, PCMK_XA_TARGET_PATTERN);
case fenced_target_by_attribute:
return crm_strdup_printf("%s=%s",
- crm_element_value(level, XML_ATTR_STONITH_TARGET_ATTRIBUTE),
- crm_element_value(level, XML_ATTR_STONITH_TARGET_VALUE));
+ crm_element_value(level, PCMK_XA_TARGET_ATTRIBUTE),
+ crm_element_value(level, PCMK_XA_TARGET_VALUE));
default:
- return crm_strdup_printf("unknown-%s", ID(level));
+ return crm_strdup_printf("unknown-%s", pcmk__xe_id(level));
}
}
@@ -1604,15 +1612,15 @@ stonith_level_key(const xmlNode *level, enum fenced_target_by mode)
static enum fenced_target_by
unpack_level_kind(const xmlNode *level)
{
- if (crm_element_value(level, XML_ATTR_STONITH_TARGET) != NULL) {
+ if (crm_element_value(level, PCMK_XA_TARGET) != NULL) {
return fenced_target_by_name;
}
- if (crm_element_value(level, XML_ATTR_STONITH_TARGET_PATTERN) != NULL) {
+ if (crm_element_value(level, PCMK_XA_TARGET_PATTERN) != NULL) {
return fenced_target_by_pattern;
}
if (!stand_alone /* if standalone, there's no attribute manager */
- && (crm_element_value(level, XML_ATTR_STONITH_TARGET_ATTRIBUTE) != NULL)
- && (crm_element_value(level, XML_ATTR_STONITH_TARGET_VALUE) != NULL)) {
+ && (crm_element_value(level, PCMK_XA_TARGET_ATTRIBUTE) != NULL)
+ && (crm_element_value(level, PCMK_XA_TARGET_VALUE) != NULL)) {
return fenced_target_by_attribute;
}
return fenced_target_by_unknown;
@@ -1670,8 +1678,8 @@ unpack_level_request(xmlNode *xml, enum fenced_target_by *mode, char **target,
* search by xpath, because it might give multiple hits if the XML is the
* CIB.
*/
- if ((xml != NULL) && !pcmk__xe_is(xml, XML_TAG_FENCING_LEVEL)) {
- xml = get_xpath_object("//" XML_TAG_FENCING_LEVEL, xml, LOG_WARNING);
+ if ((xml != NULL) && !pcmk__xe_is(xml, PCMK_XE_FENCING_LEVEL)) {
+ xml = get_xpath_object("//" PCMK_XE_FENCING_LEVEL, xml, LOG_WARNING);
}
if (xml == NULL) {
@@ -1681,7 +1689,7 @@ unpack_level_request(xmlNode *xml, enum fenced_target_by *mode, char **target,
} else {
local_mode = unpack_level_kind(xml);
local_target = stonith_level_key(xml, local_mode);
- crm_element_value_int(xml, XML_ATTR_STONITH_INDEX, &local_id);
+ crm_element_value_int(xml, PCMK_XA_INDEX, &local_id);
if (desc != NULL) {
*desc = crm_strdup_printf("%s[%d]", local_target, local_id);
}
@@ -1737,7 +1745,7 @@ fenced_register_level(xmlNode *msg, char **desc, pcmk__action_result_t *result)
}
// Ensure an ID was given (even the client API adds an ID)
- if (pcmk__str_empty(ID(level))) {
+ if (pcmk__str_empty(pcmk__xe_id(level))) {
crm_warn("Ignoring registration for topology level without ID");
free(target);
crm_log_xml_trace(level, "Bad level");
@@ -1749,12 +1757,12 @@ fenced_register_level(xmlNode *msg, char **desc, pcmk__action_result_t *result)
// Ensure a valid target was specified
if (mode == fenced_target_by_unknown) {
crm_warn("Ignoring registration for topology level '%s' "
- "without valid target", ID(level));
+ "without valid target", pcmk__xe_id(level));
free(target);
crm_log_xml_trace(level, "Bad level");
pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
"Invalid target for topology level '%s'",
- ID(level));
+ pcmk__xe_id(level));
return;
}
@@ -1766,28 +1774,24 @@ fenced_register_level(xmlNode *msg, char **desc, pcmk__action_result_t *result)
crm_log_xml_trace(level, "Bad level");
pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
"Invalid level number '%s' for topology level '%s'",
- pcmk__s(crm_element_value(level,
- XML_ATTR_STONITH_INDEX),
+ pcmk__s(crm_element_value(level, PCMK_XA_INDEX),
""),
- ID(level));
+ pcmk__xe_id(level));
return;
}
/* Find or create topology table entry */
tp = g_hash_table_lookup(topology, target);
if (tp == NULL) {
- tp = calloc(1, sizeof(stonith_topology_t));
- if (tp == NULL) {
- pcmk__set_result(result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
- strerror(ENOMEM));
- free(target);
- return;
- }
+ tp = pcmk__assert_alloc(1, sizeof(stonith_topology_t));
+
tp->kind = mode;
tp->target = target;
- tp->target_value = crm_element_value_copy(level, XML_ATTR_STONITH_TARGET_VALUE);
- tp->target_pattern = crm_element_value_copy(level, XML_ATTR_STONITH_TARGET_PATTERN);
- tp->target_attribute = crm_element_value_copy(level, XML_ATTR_STONITH_TARGET_ATTRIBUTE);
+ tp->target_value = crm_element_value_copy(level, PCMK_XA_TARGET_VALUE);
+ tp->target_pattern = crm_element_value_copy(level,
+ PCMK_XA_TARGET_PATTERN);
+ tp->target_attribute = crm_element_value_copy(level,
+ PCMK_XA_TARGET_ATTRIBUTE);
g_hash_table_replace(topology, tp->target, tp);
crm_trace("Added %s (%d) to the topology (%d active entries)",
@@ -1801,12 +1805,12 @@ fenced_register_level(xmlNode *msg, char **desc, pcmk__action_result_t *result)
tp->target, id);
}
- devices = parse_device_list(crm_element_value(level, XML_ATTR_STONITH_DEVICES));
+ devices = parse_device_list(crm_element_value(level, PCMK_XA_DEVICES));
for (dIter = devices; dIter; dIter = dIter->next) {
const char *device = dIter->value;
crm_trace("Adding device '%s' for %s[%d]", device, tp->target, id);
- tp->levels[id] = g_list_append(tp->levels[id], strdup(device));
+ tp->levels[id] = g_list_append(tp->levels[id], pcmk__str_copy(device));
}
stonith_key_value_freeall(devices, 1, 1);
@@ -1857,12 +1861,11 @@ fenced_unregister_level(xmlNode *msg, char **desc,
crm_log_xml_trace(level, "Bad level");
pcmk__format_result(result, CRM_EX_INVALID_PARAM, PCMK_EXEC_INVALID,
"Invalid level number '%s' for topology level %s",
- pcmk__s(crm_element_value(level,
- XML_ATTR_STONITH_INDEX),
+ pcmk__s(crm_element_value(level, PCMK_XA_INDEX),
"<null>"),
// Client API doesn't add ID to unregistration XML
- pcmk__s(ID(level), ""));
+ pcmk__s(pcmk__xe_id(level), ""));
return;
}
@@ -1906,26 +1909,29 @@ list_to_string(GList *list, const char *delim, gboolean terminate_with_delim)
char *rv;
GList *gIter;
+ char *pos = NULL;
+ const char *lead_delim = "";
+
for (gIter = list; gIter != NULL; gIter = gIter->next) {
const char *value = (const char *) gIter->data;
alloc_size += strlen(value);
}
- rv = calloc(alloc_size, sizeof(char));
- if (rv) {
- char *pos = rv;
- const char *lead_delim = "";
- for (gIter = list; gIter != NULL; gIter = gIter->next) {
- const char *value = (const char *) gIter->data;
+ rv = pcmk__assert_alloc(alloc_size, sizeof(char));
+ pos = rv;
- pos = &pos[sprintf(pos, "%s%s", lead_delim, value)];
- lead_delim = delim;
- }
- if (max && terminate_with_delim) {
- sprintf(pos, "%s", delim);
- }
+ for (gIter = list; gIter != NULL; gIter = gIter->next) {
+ const char *value = (const char *) gIter->data;
+
+ pos = &pos[sprintf(pos, "%s%s", lead_delim, value)];
+ lead_delim = delim;
+ }
+
+ if (max && terminate_with_delim) {
+ sprintf(pos, "%s", delim);
}
+
return rv;
}
@@ -1947,10 +1953,11 @@ list_to_string(GList *list, const char *delim, gboolean terminate_with_delim)
static void
execute_agent_action(xmlNode *msg, pcmk__action_result_t *result)
{
- xmlNode *dev = get_xpath_object("//" F_STONITH_DEVICE, msg, LOG_ERR);
- xmlNode *op = get_xpath_object("//@" F_STONITH_ACTION, msg, LOG_ERR);
- const char *id = crm_element_value(dev, F_STONITH_DEVICE);
- const char *action = crm_element_value(op, F_STONITH_ACTION);
+ xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, msg, LOG_ERR);
+ xmlNode *op = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, msg,
+ LOG_ERR);
+ const char *id = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
+ const char *action = crm_element_value(op, PCMK__XA_ST_DEVICE_ACTION);
async_command_t *cmd = NULL;
stonith_device_t *device = NULL;
@@ -2023,7 +2030,8 @@ search_devices_record_result(struct device_search_s *search, const char *device,
return;
}
}
- search->capable = g_list_append(search->capable, strdup(device));
+ search->capable = g_list_append(search->capable,
+ pcmk__str_copy(device));
}
if (search->replies_needed == search->replies_received) {
@@ -2158,10 +2166,12 @@ can_fence_host_with_device(stonith_device_t *dev,
// Check eligibility as specified by pcmk_host_check
check_type = target_list_type(dev);
alias = g_hash_table_lookup(dev->aliases, target);
- if (pcmk__str_eq(check_type, PCMK__VALUE_NONE, pcmk__str_casei)) {
+ if (pcmk__str_eq(check_type, PCMK_VALUE_NONE, pcmk__str_casei)) {
can = TRUE;
- } else if (pcmk__str_eq(check_type, "static-list", pcmk__str_casei)) {
+ } else if (pcmk__str_eq(check_type, PCMK_VALUE_STATIC_LIST,
+ pcmk__str_casei)) {
+
if (pcmk__str_in_list(target, dev->targets, pcmk__str_casei)) {
can = TRUE;
} else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)
@@ -2169,7 +2179,8 @@ can_fence_host_with_device(stonith_device_t *dev,
can = TRUE;
}
- } else if (pcmk__str_eq(check_type, "dynamic-list", pcmk__str_casei)) {
+ } else if (pcmk__str_eq(check_type, PCMK_VALUE_DYNAMIC_LIST,
+ pcmk__str_casei)) {
time_t now = time(NULL);
if (dev->targets == NULL || dev->targets_age + 60 < now) {
@@ -2177,8 +2188,10 @@ can_fence_host_with_device(stonith_device_t *dev,
search->per_device_timeout);
if (device_timeout > search->per_device_timeout) {
- crm_notice("Since the pcmk_list_timeout(%ds) parameter of %s is larger than stonith-timeout(%ds), timeout may occur",
- device_timeout, dev_id, search->per_device_timeout);
+ crm_notice("Since the pcmk_list_timeout (%ds) parameter of %s "
+ "is larger than " PCMK_OPT_STONITH_TIMEOUT
+ " (%ds), timeout may occur",
+ device_timeout, dev_id, search->per_device_timeout);
}
crm_trace("Running '%s' to check whether %s is eligible to fence %s (%s)",
@@ -2196,12 +2209,14 @@ can_fence_host_with_device(stonith_device_t *dev,
can = TRUE;
}
- } else if (pcmk__str_eq(check_type, "status", pcmk__str_casei)) {
+ } else if (pcmk__str_eq(check_type, PCMK_VALUE_STATUS, pcmk__str_casei)) {
int device_timeout = get_action_timeout(dev, check_type, search->per_device_timeout);
if (device_timeout > search->per_device_timeout) {
- crm_notice("Since the pcmk_status_timeout(%ds) parameter of %s is larger than stonith-timeout(%ds), timeout may occur",
- device_timeout, dev_id, search->per_device_timeout);
+ crm_notice("Since the pcmk_status_timeout (%ds) parameter of %s is "
+ "larger than " PCMK_OPT_STONITH_TIMEOUT " (%ds), "
+ "timeout may occur",
+ device_timeout, dev_id, search->per_device_timeout);
}
crm_trace("Running '%s' to check whether %s is eligible to fence %s (%s)",
@@ -2246,16 +2261,10 @@ get_capable_devices(const char *host, const char *action, int timeout, bool suic
return;
}
- search = calloc(1, sizeof(struct device_search_s));
- if (!search) {
- crm_crit("Cannot search for capable fence devices: %s",
- strerror(ENOMEM));
- callback(NULL, user_data);
- return;
- }
+ search = pcmk__assert_alloc(1, sizeof(struct device_search_s));
- pcmk__str_update(&search->host, host);
- pcmk__str_update(&search->action, action);
+ search->host = pcmk__str_copy(host);
+ search->action = pcmk__str_copy(action);
search->per_device_timeout = timeout;
search->allow_suicide = suicide;
search->callback = callback;
@@ -2303,28 +2312,31 @@ add_action_specific_attributes(xmlNode *xml, const char *action,
CRM_CHECK(xml && action && device, return);
+ // PCMK__XA_ST_REQUIRED is currently used only for unfencing
if (is_action_required(action, device)) {
crm_trace("Action '%s' is required using %s", action, device->id);
- crm_xml_add_int(xml, F_STONITH_DEVICE_REQUIRED, 1);
+ crm_xml_add_int(xml, PCMK__XA_ST_REQUIRED, 1);
}
+ // pcmk_<action>_timeout if configured
action_specific_timeout = get_action_timeout(device, action, 0);
if (action_specific_timeout) {
- crm_trace("Action '%s' has timeout %dms using %s",
+ crm_trace("Action '%s' has timeout %ds using %s",
action, action_specific_timeout, device->id);
- crm_xml_add_int(xml, F_STONITH_ACTION_TIMEOUT, action_specific_timeout);
+ crm_xml_add_int(xml, PCMK__XA_ST_ACTION_TIMEOUT,
+ action_specific_timeout);
}
delay_max = get_action_delay_max(device, action);
if (delay_max > 0) {
crm_trace("Action '%s' has maximum random delay %ds using %s",
action, delay_max, device->id);
- crm_xml_add_int(xml, F_STONITH_DELAY_MAX, delay_max);
+ crm_xml_add_int(xml, PCMK__XA_ST_DELAY_MAX, delay_max);
}
delay_base = get_action_delay_base(device, action, target);
if (delay_base > 0) {
- crm_xml_add_int(xml, F_STONITH_DELAY_BASE, delay_base);
+ crm_xml_add_int(xml, PCMK__XA_ST_DELAY_BASE, delay_base);
}
if ((delay_max > 0) && (delay_base == 0)) {
@@ -2357,7 +2369,7 @@ add_disallowed(xmlNode *xml, const char *action, const stonith_device_t *device,
if (!localhost_is_eligible(device, action, target, allow_suicide)) {
crm_trace("Action '%s' using %s is disallowed for local host",
action, device->id);
- pcmk__xe_set_bool_attr(xml, F_STONITH_ACTION_DISALLOWED, true);
+ pcmk__xe_set_bool_attr(xml, PCMK__XA_ST_ACTION_DISALLOWED, true);
}
}
@@ -2376,9 +2388,9 @@ add_action_reply(xmlNode *xml, const char *action,
const stonith_device_t *device, const char *target,
gboolean allow_suicide)
{
- xmlNode *child = create_xml_node(xml, F_STONITH_ACTION);
+ xmlNode *child = pcmk__xe_create(xml, PCMK__XE_ST_DEVICE_ACTION);
- crm_xml_add(child, XML_ATTR_ID, action);
+ crm_xml_add(child, PCMK_XA_ID, action);
add_action_specific_attributes(child, action, device, target);
add_disallowed(child, action, device, target, allow_suicide);
}
@@ -2402,8 +2414,11 @@ stonith_send_reply(const xmlNode *reply, int call_options,
if (remote_peer == NULL) {
do_local_reply(reply, client, call_options);
} else {
- send_cluster_message(crm_get_peer(0, remote_peer), crm_msg_stonith_ng,
- reply, FALSE);
+ const crm_node_t *node =
+ pcmk__get_node(0, remote_peer, NULL,
+ pcmk__node_search_cluster_member);
+
+ pcmk__cluster_send_message(node, crm_msg_stonith_ng, reply);
}
}
@@ -2412,7 +2427,7 @@ stonith_query_capable_device_cb(GList * devices, void *user_data)
{
struct st_query_data *query = user_data;
int available_devices = 0;
- xmlNode *dev = NULL;
+ xmlNode *wrapper = NULL;
xmlNode *list = NULL;
GList *lpc = NULL;
pcmk__client_t *client = NULL;
@@ -2426,12 +2441,15 @@ stonith_query_capable_device_cb(GList * devices, void *user_data)
}
}
- /* Pack the results into XML */
- list = create_xml_node(NULL, __func__);
- crm_xml_add(list, F_STONITH_TARGET, query->target);
+ // Pack the results into XML
+ wrapper = pcmk__xe_create(query->reply, PCMK__XE_ST_CALLDATA);
+ list = pcmk__xe_create(wrapper, __func__);
+ crm_xml_add(list, PCMK__XA_ST_TARGET, query->target);
+
for (lpc = devices; lpc != NULL; lpc = lpc->next) {
stonith_device_t *device = g_hash_table_lookup(device_list, lpc->data);
const char *action = query->action;
+ xmlNode *dev = NULL;
if (!device) {
/* It is possible the device got unregistered while
@@ -2441,12 +2459,15 @@ stonith_query_capable_device_cb(GList * devices, void *user_data)
available_devices++;
- dev = create_xml_node(list, F_STONITH_DEVICE);
- crm_xml_add(dev, XML_ATTR_ID, device->id);
- crm_xml_add(dev, "namespace", device->namespace);
- crm_xml_add(dev, "agent", device->agent);
- crm_xml_add_int(dev, F_STONITH_DEVICE_VERIFIED, device->verified);
- crm_xml_add_int(dev, F_STONITH_DEVICE_SUPPORT_FLAGS, device->flags);
+ dev = pcmk__xe_create(list, PCMK__XE_ST_DEVICE_ID);
+ crm_xml_add(dev, PCMK_XA_ID, device->id);
+ crm_xml_add(dev, PCMK__XA_NAMESPACE, device->namespace);
+ crm_xml_add(dev, PCMK_XA_AGENT, device->agent);
+
+ // Has had successful monitor, list, or status on this node
+ crm_xml_add_int(dev, PCMK__XA_ST_MONITOR_VERIFIED, device->verified);
+
+ crm_xml_add_int(dev, PCMK__XA_ST_DEVICE_SUPPORT_FLAGS, device->flags);
/* If the originating fencer wants to reboot the node, and we have a
* capable device that doesn't support "reboot", remap to "off" instead.
@@ -2482,13 +2503,13 @@ stonith_query_capable_device_cb(GList * devices, void *user_data)
/* A query without a target wants device parameters */
if (query->target == NULL) {
- xmlNode *attrs = create_xml_node(dev, XML_TAG_ATTRS);
+ xmlNode *attrs = pcmk__xe_create(dev, PCMK__XE_ATTRIBUTES);
g_hash_table_foreach(device->params, hash2field, attrs);
}
}
- crm_xml_add_int(list, F_STONITH_AVAILABLE_DEVICES, available_devices);
+ crm_xml_add_int(list, PCMK__XA_ST_AVAILABLE_DEVICES, available_devices);
if (query->target) {
crm_debug("Found %d matching device%s for target '%s'",
available_devices, pcmk__plural_s(available_devices),
@@ -2498,10 +2519,7 @@ stonith_query_capable_device_cb(GList * devices, void *user_data)
available_devices, pcmk__plural_s(available_devices));
}
- if (list != NULL) {
- crm_log_xml_trace(list, "Add query results");
- add_message_xml(query->reply, F_STONITH_CALLDATA, list);
- }
+ crm_log_xml_trace(list, "query-result");
stonith_send_reply(query->reply, query->call_options, query->remote_peer,
client);
@@ -2513,7 +2531,6 @@ done:
free(query->target);
free(query->action);
free(query);
- free_xml(list);
g_list_free_full(devices, free);
}
@@ -2542,14 +2559,16 @@ log_async_result(const async_command_t *cmd,
if (pcmk__result_ok(result)) {
log_level = (cmd->target == NULL)? LOG_DEBUG : LOG_NOTICE;
if ((result->action_stdout != NULL)
- && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_none)) {
+ && !pcmk__str_eq(cmd->action, PCMK_ACTION_METADATA,
+ pcmk__str_none)) {
output_log_level = LOG_DEBUG;
}
next = NULL;
} else {
log_level = (cmd->target == NULL)? LOG_NOTICE : LOG_ERR;
if ((result->action_stdout != NULL)
- && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_none)) {
+ && !pcmk__str_eq(cmd->action, PCMK_ACTION_METADATA,
+ pcmk__str_none)) {
output_log_level = LOG_WARNING;
}
}
@@ -2633,7 +2652,7 @@ send_async_reply(const async_command_t *cmd, const pcmk__action_result_t *result
reply = construct_async_reply(cmd, result);
if (merged) {
- pcmk__xe_set_bool_attr(reply, F_STONITH_MERGED, true);
+ pcmk__xe_set_bool_attr(reply, PCMK__XA_ST_OP_MERGED, true);
}
if (!stand_alone && pcmk__is_fencing_action(cmd->action)
@@ -2643,9 +2662,9 @@ send_async_reply(const async_command_t *cmd, const pcmk__action_result_t *result
*/
crm_trace("Broadcast '%s' result for %s (target was also originator)",
cmd->action, cmd->target);
- crm_xml_add(reply, F_SUBTYPE, "broadcast");
- crm_xml_add(reply, F_STONITH_OPERATION, T_STONITH_NOTIFY);
- send_cluster_message(NULL, crm_msg_stonith_ng, reply, FALSE);
+ crm_xml_add(reply, PCMK__XA_SUBT, PCMK__VALUE_BROADCAST);
+ crm_xml_add(reply, PCMK__XA_ST_OP, STONITH_OP_NOTIFY);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, reply);
} else {
// Reply only to the originator
stonith_send_reply(reply, cmd->options, cmd->origin, client);
@@ -2656,18 +2675,19 @@ send_async_reply(const async_command_t *cmd, const pcmk__action_result_t *result
if (stand_alone) {
/* Do notification with a clean data object */
- xmlNode *notify_data = create_xml_node(NULL, T_STONITH_NOTIFY_FENCE);
+ xmlNode *notify_data = pcmk__xe_create(NULL, PCMK__XE_ST_NOTIFY_FENCE);
stonith__xe_set_result(notify_data, result);
- crm_xml_add(notify_data, F_STONITH_TARGET, cmd->target);
- crm_xml_add(notify_data, F_STONITH_OPERATION, cmd->op);
- crm_xml_add(notify_data, F_STONITH_DELEGATE, "localhost");
- crm_xml_add(notify_data, F_STONITH_DEVICE, cmd->device);
- crm_xml_add(notify_data, F_STONITH_REMOTE_OP_ID, cmd->remote_op_id);
- crm_xml_add(notify_data, F_STONITH_ORIGIN, cmd->client);
+ crm_xml_add(notify_data, PCMK__XA_ST_TARGET, cmd->target);
+ crm_xml_add(notify_data, PCMK__XA_ST_OP, cmd->op);
+ crm_xml_add(notify_data, PCMK__XA_ST_DELEGATE, "localhost");
+ crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ID, cmd->device);
+ crm_xml_add(notify_data, PCMK__XA_ST_REMOTE_OP, cmd->remote_op_id);
+ crm_xml_add(notify_data, PCMK__XA_ST_ORIGIN, cmd->client);
- fenced_send_notification(T_STONITH_NOTIFY_FENCE, result, notify_data);
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_FENCE, result,
+ notify_data);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
}
}
@@ -2890,7 +2910,7 @@ fence_locally(xmlNode *msg, pcmk__action_result_t *result)
CRM_CHECK((msg != NULL) && (result != NULL), return);
- dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_ERR);
+ dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, msg, LOG_ERR);
cmd = create_async_command(msg);
if (cmd == NULL) {
@@ -2899,7 +2919,7 @@ fence_locally(xmlNode *msg, pcmk__action_result_t *result)
return;
}
- device_id = crm_element_value(dev, F_STONITH_DEVICE);
+ device_id = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
if (device_id != NULL) {
device = g_hash_table_lookup(device_list, device_id);
if (device == NULL) {
@@ -2911,14 +2931,16 @@ fence_locally(xmlNode *msg, pcmk__action_result_t *result)
schedule_stonith_command(cmd, device);
} else {
- const char *host = crm_element_value(dev, F_STONITH_TARGET);
+ const char *host = crm_element_value(dev, PCMK__XA_ST_TARGET);
if (pcmk_is_set(cmd->options, st_opt_cs_nodeid)) {
int nodeid = 0;
crm_node_t *node = NULL;
pcmk__scan_min_int(host, &nodeid, 0);
- node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
+ node = pcmk__search_node_caches(nodeid, NULL,
+ pcmk__node_search_any
+ |pcmk__node_search_cluster_cib);
if (node != NULL) {
host = node->uname;
}
@@ -2953,10 +2975,10 @@ fenced_construct_reply(const xmlNode *request, xmlNode *data,
{
xmlNode *reply = NULL;
- reply = create_xml_node(NULL, T_STONITH_REPLY);
+ reply = pcmk__xe_create(NULL, PCMK__XE_ST_REPLY);
- crm_xml_add(reply, "st_origin", __func__);
- crm_xml_add(reply, F_TYPE, T_STONITH_NG);
+ crm_xml_add(reply, PCMK__XA_ST_ORIGIN, __func__);
+ crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
stonith__xe_set_result(reply, result);
if (request == NULL) {
@@ -2976,12 +2998,12 @@ fenced_construct_reply(const xmlNode *request, xmlNode *data,
// Attributes to copy from request to reply
const char *names[] = {
- F_STONITH_OPERATION,
- F_STONITH_CALLID,
- F_STONITH_CLIENTID,
- F_STONITH_CLIENTNAME,
- F_STONITH_REMOTE_OP_ID,
- F_STONITH_CALLOPTS
+ PCMK__XA_ST_OP,
+ PCMK__XA_ST_CALLID,
+ PCMK__XA_ST_CLIENTID,
+ PCMK__XA_ST_CLIENTNAME,
+ PCMK__XA_ST_REMOTE_OP,
+ PCMK__XA_ST_CALLOPT,
};
for (int lpc = 0; lpc < PCMK__NELEM(names); lpc++) {
@@ -2990,7 +3012,9 @@ fenced_construct_reply(const xmlNode *request, xmlNode *data,
crm_xml_add(reply, name, value);
}
if (data != NULL) {
- add_message_xml(reply, F_STONITH_CALLDATA, data);
+ xmlNode *wrapper = pcmk__xe_create(reply, PCMK__XE_ST_CALLDATA);
+
+ pcmk__xml_copy(wrapper, data);
}
}
return reply;
@@ -3007,20 +3031,20 @@ static xmlNode *
construct_async_reply(const async_command_t *cmd,
const pcmk__action_result_t *result)
{
- xmlNode *reply = create_xml_node(NULL, T_STONITH_REPLY);
-
- crm_xml_add(reply, "st_origin", __func__);
- crm_xml_add(reply, F_TYPE, T_STONITH_NG);
- crm_xml_add(reply, F_STONITH_OPERATION, cmd->op);
- crm_xml_add(reply, F_STONITH_DEVICE, cmd->device);
- crm_xml_add(reply, F_STONITH_REMOTE_OP_ID, cmd->remote_op_id);
- crm_xml_add(reply, F_STONITH_CLIENTID, cmd->client);
- crm_xml_add(reply, F_STONITH_CLIENTNAME, cmd->client_name);
- crm_xml_add(reply, F_STONITH_TARGET, cmd->target);
- crm_xml_add(reply, F_STONITH_ACTION, cmd->op);
- crm_xml_add(reply, F_STONITH_ORIGIN, cmd->origin);
- crm_xml_add_int(reply, F_STONITH_CALLID, cmd->id);
- crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options);
+ xmlNode *reply = pcmk__xe_create(NULL, PCMK__XE_ST_REPLY);
+
+ crm_xml_add(reply, PCMK__XA_ST_ORIGIN, __func__);
+ crm_xml_add(reply, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
+ crm_xml_add(reply, PCMK__XA_ST_OP, cmd->op);
+ crm_xml_add(reply, PCMK__XA_ST_DEVICE_ID, cmd->device);
+ crm_xml_add(reply, PCMK__XA_ST_REMOTE_OP, cmd->remote_op_id);
+ crm_xml_add(reply, PCMK__XA_ST_CLIENTID, cmd->client);
+ crm_xml_add(reply, PCMK__XA_ST_CLIENTNAME, cmd->client_name);
+ crm_xml_add(reply, PCMK__XA_ST_TARGET, cmd->target);
+ crm_xml_add(reply, PCMK__XA_ST_DEVICE_ACTION, cmd->op);
+ crm_xml_add(reply, PCMK__XA_ST_ORIGIN, cmd->origin);
+ crm_xml_add_int(reply, PCMK__XA_ST_CALLID, cmd->id);
+ crm_xml_add_int(reply, PCMK__XA_ST_CALLOPT, cmd->options);
stonith__xe_set_result(reply, result);
return reply;
@@ -3081,7 +3105,8 @@ check_alternate_host(const char *target)
static void
remove_relay_op(xmlNode * request)
{
- xmlNode *dev = get_xpath_object("//@" F_STONITH_ACTION, request, LOG_TRACE);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, request,
+ LOG_TRACE);
const char *relay_op_id = NULL;
const char *op_id = NULL;
const char *client_name = NULL;
@@ -3089,12 +3114,12 @@ remove_relay_op(xmlNode * request)
remote_fencing_op_t *relay_op = NULL;
if (dev) {
- target = crm_element_value(dev, F_STONITH_TARGET);
+ target = crm_element_value(dev, PCMK__XA_ST_TARGET);
}
- relay_op_id = crm_element_value(request, F_STONITH_REMOTE_OP_ID_RELAY);
- op_id = crm_element_value(request, F_STONITH_REMOTE_OP_ID);
- client_name = crm_element_value(request, F_STONITH_CLIENTNAME);
+ relay_op_id = crm_element_value(request, PCMK__XA_ST_REMOTE_OP_RELAY);
+ op_id = crm_element_value(request, PCMK__XA_ST_REMOTE_OP);
+ client_name = crm_element_value(request, PCMK__XA_ST_CLIENTNAME);
/* Delete RELAY operation. */
if (relay_op_id && target && pcmk__str_eq(target, stonith_our_uname, pcmk__str_casei)) {
@@ -3162,11 +3187,11 @@ is_privileged(const pcmk__client_t *c, const char *op)
static xmlNode *
handle_register_request(pcmk__request_t *request)
{
- xmlNode *reply = create_xml_node(NULL, "reply");
+ xmlNode *reply = pcmk__xe_create(NULL, "reply");
CRM_ASSERT(request->ipc_client != NULL);
- crm_xml_add(reply, F_STONITH_OPERATION, CRM_OP_REGISTER);
- crm_xml_add(reply, F_STONITH_CLIENTID, request->ipc_client->id);
+ crm_xml_add(reply, PCMK__XA_ST_OP, CRM_OP_REGISTER);
+ crm_xml_add(reply, PCMK__XA_ST_CLIENTID, request->ipc_client->id);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
pcmk__set_request_flags(request, pcmk__request_reuse_options);
return reply;
@@ -3187,11 +3212,12 @@ handle_agent_request(pcmk__request_t *request)
static xmlNode *
handle_update_timeout_request(pcmk__request_t *request)
{
- const char *call_id = crm_element_value(request->xml, F_STONITH_CALLID);
- const char *client_id = crm_element_value(request->xml, F_STONITH_CLIENTID);
+ const char *call_id = crm_element_value(request->xml, PCMK__XA_ST_CALLID);
+ const char *client_id = crm_element_value(request->xml,
+ PCMK__XA_ST_CLIENTID);
int op_timeout = 0;
- crm_element_value_int(request->xml, F_STONITH_TIMEOUT, &op_timeout);
+ crm_element_value_int(request->xml, PCMK__XA_ST_TIMEOUT, &op_timeout);
do_stonith_async_timeout_update(client_id, call_id, op_timeout);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
return NULL;
@@ -3205,7 +3231,8 @@ handle_query_request(pcmk__request_t *request)
xmlNode *dev = NULL;
const char *action = NULL;
const char *target = NULL;
- const char *client_id = crm_element_value(request->xml, F_STONITH_CLIENTID);
+ const char *client_id = crm_element_value(request->xml,
+ PCMK__XA_ST_CLIENTID);
struct st_query_data *query = NULL;
if (request->peer != NULL) {
@@ -3218,51 +3245,51 @@ handle_query_request(pcmk__request_t *request)
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
- dev = get_xpath_object("//@" F_STONITH_ACTION, request->xml, LOG_NEVER);
+ dev = get_xpath_object("//@" PCMK__XE_ST_DEVICE_ACTION, request->xml,
+ LOG_NEVER);
if (dev != NULL) {
- const char *device = crm_element_value(dev, F_STONITH_DEVICE);
+ const char *device = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
if (pcmk__str_eq(device, "manual_ack", pcmk__str_casei)) {
return NULL; // No query or reply necessary
}
- target = crm_element_value(dev, F_STONITH_TARGET);
- action = crm_element_value(dev, F_STONITH_ACTION);
+ target = crm_element_value(dev, PCMK__XA_ST_TARGET);
+ action = crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION);
}
crm_log_xml_trace(request->xml, "Query");
- query = calloc(1, sizeof(struct st_query_data));
- CRM_ASSERT(query != NULL);
+ query = pcmk__assert_alloc(1, sizeof(struct st_query_data));
query->reply = fenced_construct_reply(request->xml, NULL, &request->result);
- pcmk__str_update(&query->remote_peer, request->peer);
- pcmk__str_update(&query->client_id, client_id);
- pcmk__str_update(&query->target, target);
- pcmk__str_update(&query->action, action);
+ query->remote_peer = pcmk__str_copy(request->peer);
+ query->client_id = pcmk__str_copy(client_id);
+ query->target = pcmk__str_copy(target);
+ query->action = pcmk__str_copy(action);
query->call_options = request->call_options;
- crm_element_value_int(request->xml, F_STONITH_TIMEOUT, &timeout);
+ crm_element_value_int(request->xml, PCMK__XA_ST_TIMEOUT, &timeout);
get_capable_devices(target, action, timeout,
pcmk_is_set(query->call_options, st_opt_allow_suicide),
query, stonith_query_capable_device_cb, st_device_supports_none);
return NULL;
}
-// T_STONITH_NOTIFY
+// STONITH_OP_NOTIFY
static xmlNode *
handle_notify_request(pcmk__request_t *request)
{
const char *flag_name = NULL;
CRM_ASSERT(request->ipc_client != NULL);
- flag_name = crm_element_value(request->xml, F_STONITH_NOTIFY_ACTIVATE);
+ flag_name = crm_element_value(request->xml, PCMK__XA_ST_NOTIFY_ACTIVATE);
if (flag_name != NULL) {
crm_debug("Enabling %s callbacks for client %s",
flag_name, pcmk__request_origin(request));
pcmk__set_client_flags(request->ipc_client, get_stonith_flag(flag_name));
}
- flag_name = crm_element_value(request->xml, F_STONITH_NOTIFY_DEACTIVATE);
+ flag_name = crm_element_value(request->xml, PCMK__XA_ST_NOTIFY_DEACTIVATE);
if (flag_name != NULL) {
crm_debug("Disabling %s callbacks for client %s",
flag_name, pcmk__request_origin(request));
@@ -3273,22 +3300,23 @@ handle_notify_request(pcmk__request_t *request)
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
pcmk__set_request_flags(request, pcmk__request_reuse_options);
- return pcmk__ipc_create_ack(request->ipc_flags, "ack", NULL, CRM_EX_OK);
+ return pcmk__ipc_create_ack(request->ipc_flags, PCMK__XE_ACK, NULL,
+ CRM_EX_OK);
}
// STONITH_OP_RELAY
static xmlNode *
handle_relay_request(pcmk__request_t *request)
{
- xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, request->xml,
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, request->xml,
LOG_TRACE);
crm_notice("Received forwarded fencing request from "
"%s %s to fence (%s) peer %s",
pcmk__request_origin_type(request),
pcmk__request_origin(request),
- crm_element_value(dev, F_STONITH_ACTION),
- crm_element_value(dev, F_STONITH_TARGET));
+ crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION),
+ crm_element_value(dev, PCMK__XA_ST_TARGET));
if (initiate_remote_stonith_op(NULL, request->xml, FALSE) == NULL) {
fenced_set_protocol_error(&request->result);
@@ -3324,11 +3352,11 @@ handle_fence_request(pcmk__request_t *request)
} else {
const char *alternate_host = NULL;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, request->xml,
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, request->xml,
LOG_TRACE);
- const char *target = crm_element_value(dev, F_STONITH_TARGET);
- const char *action = crm_element_value(dev, F_STONITH_ACTION);
- const char *device = crm_element_value(dev, F_STONITH_DEVICE);
+ const char *target = crm_element_value(dev, PCMK__XA_ST_TARGET);
+ const char *action = crm_element_value(dev, PCMK__XA_ST_DEVICE_ACTION);
+ const char *device = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
if (request->ipc_client != NULL) {
int tolerance = 0;
@@ -3336,7 +3364,7 @@ handle_fence_request(pcmk__request_t *request)
crm_notice("Client %s wants to fence (%s) %s using %s",
pcmk__request_origin(request), action,
target, (device? device : "any device"));
- crm_element_value_int(dev, F_STONITH_TOLERANCE, &tolerance);
+ crm_element_value_int(dev, PCMK__XA_ST_TOLERANCE, &tolerance);
if (stonith_check_fence_tolerance(tolerance, target, action)) {
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE,
NULL);
@@ -3354,9 +3382,12 @@ handle_fence_request(pcmk__request_t *request)
if (alternate_host != NULL) {
const char *client_id = NULL;
remote_fencing_op_t *op = NULL;
+ crm_node_t *node = pcmk__get_node(0, alternate_host, NULL,
+ pcmk__node_search_cluster_member);
if (request->ipc_client->id == 0) {
- client_id = crm_element_value(request->xml, F_STONITH_CLIENTID);
+ client_id = crm_element_value(request->xml,
+ PCMK__XA_ST_CLIENTID);
} else {
client_id = request->ipc_client->id;
}
@@ -3367,12 +3398,11 @@ handle_fence_request(pcmk__request_t *request)
*/
op = create_remote_stonith_op(client_id, request->xml, FALSE);
- crm_xml_add(request->xml, F_STONITH_OPERATION, STONITH_OP_RELAY);
- crm_xml_add(request->xml, F_STONITH_CLIENTID,
+ crm_xml_add(request->xml, PCMK__XA_ST_OP, STONITH_OP_RELAY);
+ crm_xml_add(request->xml, PCMK__XA_ST_CLIENTID,
request->ipc_client->id);
- crm_xml_add(request->xml, F_STONITH_REMOTE_OP_ID, op->id);
- send_cluster_message(crm_get_peer(0, alternate_host),
- crm_msg_stonith_ng, request->xml, FALSE);
+ crm_xml_add(request->xml, PCMK__XA_ST_REMOTE_OP, op->id);
+ pcmk__cluster_send_message(node, crm_msg_stonith_ng, request->xml);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_PENDING,
NULL);
@@ -3416,8 +3446,8 @@ handle_history_request(pcmk__request_t *request)
static xmlNode *
handle_device_add_request(pcmk__request_t *request)
{
- const char *op = crm_element_value(request->xml, F_STONITH_OPERATION);
- xmlNode *dev = get_xpath_object("//" F_STONITH_DEVICE, request->xml,
+ const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
+ xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, request->xml,
LOG_ERR);
if (is_privileged(request->ipc_client, op)) {
@@ -3432,8 +3462,8 @@ handle_device_add_request(pcmk__request_t *request)
PCMK_EXEC_INVALID,
"Unprivileged users must register device via CIB");
}
- fenced_send_device_notification(op, &request->result,
- (dev == NULL)? NULL : ID(dev));
+ fenced_send_config_notification(op, &request->result,
+ (dev == NULL)? NULL : pcmk__xe_id(dev));
return fenced_construct_reply(request->xml, NULL, &request->result);
}
@@ -3441,10 +3471,10 @@ handle_device_add_request(pcmk__request_t *request)
static xmlNode *
handle_device_delete_request(pcmk__request_t *request)
{
- xmlNode *dev = get_xpath_object("//" F_STONITH_DEVICE, request->xml,
+ xmlNode *dev = get_xpath_object("//" PCMK__XE_ST_DEVICE_ID, request->xml,
LOG_ERR);
- const char *device_id = crm_element_value(dev, XML_ATTR_ID);
- const char *op = crm_element_value(request->xml, F_STONITH_OPERATION);
+ const char *device_id = crm_element_value(dev, PCMK_XA_ID);
+ const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
if (is_privileged(request->ipc_client, op)) {
stonith_device_remove(device_id, false);
@@ -3454,7 +3484,7 @@ handle_device_delete_request(pcmk__request_t *request)
PCMK_EXEC_INVALID,
"Unprivileged users must delete device via CIB");
}
- fenced_send_device_notification(op, &request->result, device_id);
+ fenced_send_config_notification(op, &request->result, device_id);
return fenced_construct_reply(request->xml, NULL, &request->result);
}
@@ -3463,7 +3493,7 @@ static xmlNode *
handle_level_add_request(pcmk__request_t *request)
{
char *desc = NULL;
- const char *op = crm_element_value(request->xml, F_STONITH_OPERATION);
+ const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
if (is_privileged(request->ipc_client, op)) {
fenced_register_level(request->xml, &desc, &request->result);
@@ -3473,7 +3503,7 @@ handle_level_add_request(pcmk__request_t *request)
PCMK_EXEC_INVALID,
"Unprivileged users must add level via CIB");
}
- fenced_send_level_notification(op, &request->result, desc);
+ fenced_send_config_notification(op, &request->result, desc);
free(desc);
return fenced_construct_reply(request->xml, NULL, &request->result);
}
@@ -3483,7 +3513,7 @@ static xmlNode *
handle_level_delete_request(pcmk__request_t *request)
{
char *desc = NULL;
- const char *op = crm_element_value(request->xml, F_STONITH_OPERATION);
+ const char *op = crm_element_value(request->xml, PCMK__XA_ST_OP);
if (is_privileged(request->ipc_client, op)) {
fenced_unregister_level(request->xml, &desc, &request->result);
@@ -3493,7 +3523,7 @@ handle_level_delete_request(pcmk__request_t *request)
PCMK_EXEC_INVALID,
"Unprivileged users must delete level via CIB");
}
- fenced_send_level_notification(op, &request->result, desc);
+ fenced_send_config_notification(op, &request->result, desc);
free(desc);
return fenced_construct_reply(request->xml, NULL, &request->result);
}
@@ -3505,9 +3535,9 @@ handle_cache_request(pcmk__request_t *request)
int node_id = 0;
const char *name = NULL;
- crm_element_value_int(request->xml, XML_ATTR_ID, &node_id);
- name = crm_element_value(request->xml, XML_ATTR_UNAME);
- reap_crm_member(node_id, name);
+ crm_element_value_int(request->xml, PCMK_XA_ID, &node_id);
+ name = crm_element_value(request->xml, PCMK_XA_UNAME);
+ pcmk__cluster_forget_cluster_node(node_id, name);
pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
return NULL;
}
@@ -3531,7 +3561,7 @@ fenced_register_handlers(void)
{ STONITH_OP_EXEC, handle_agent_request },
{ STONITH_OP_TIMEOUT_UPDATE, handle_update_timeout_request },
{ STONITH_OP_QUERY, handle_query_request },
- { T_STONITH_NOTIFY, handle_notify_request },
+ { STONITH_OP_NOTIFY, handle_notify_request },
{ STONITH_OP_RELAY, handle_relay_request },
{ STONITH_OP_FENCE, handle_fence_request },
{ STONITH_OP_FENCE_HISTORY, handle_history_request },
@@ -3596,12 +3626,15 @@ static void
handle_reply(pcmk__client_t *client, xmlNode *request, const char *remote_peer)
{
// Copy, because request might be freed before we want to log this
- char *op = crm_element_value_copy(request, F_STONITH_OPERATION);
+ char *op = crm_element_value_copy(request, PCMK__XA_ST_OP);
if (pcmk__str_eq(op, STONITH_OP_QUERY, pcmk__str_none)) {
process_remote_stonith_query(request);
- } else if (pcmk__str_any_of(op, T_STONITH_NOTIFY, STONITH_OP_FENCE, NULL)) {
+
+ } else if (pcmk__str_any_of(op, STONITH_OP_NOTIFY, STONITH_OP_FENCE,
+ NULL)) {
fenced_process_fencing_reply(request);
+
} else {
crm_err("Ignoring unknown %s reply from %s %s",
pcmk__s(op, "untyped"), ((client == NULL)? "peer" : "client"),
@@ -3635,13 +3668,13 @@ stonith_command(pcmk__client_t *client, uint32_t id, uint32_t flags,
CRM_CHECK(message != NULL, return);
- if (get_xpath_object("//" T_STONITH_REPLY, message, LOG_NEVER) != NULL) {
+ if (get_xpath_object("//" PCMK__XE_ST_REPLY, message, LOG_NEVER) != NULL) {
is_reply = true;
}
- crm_element_value_int(message, F_STONITH_CALLOPTS, &call_options);
+ crm_element_value_int(message, PCMK__XA_ST_CALLOPT, &call_options);
crm_debug("Processing %ssynchronous %s %s %u from %s %s",
pcmk_is_set(call_options, st_opt_sync_call)? "" : "a",
- crm_element_value(message, F_STONITH_OPERATION),
+ crm_element_value(message, PCMK__XA_ST_OP),
(is_reply? "reply" : "request"), id,
((client == NULL)? "peer" : "client"),
((client == NULL)? remote_peer : pcmk__client_name(client)));
@@ -3663,7 +3696,7 @@ stonith_command(pcmk__client_t *client, uint32_t id, uint32_t flags,
.result = PCMK__UNKNOWN_RESULT,
};
- request.op = crm_element_value_copy(request.xml, F_STONITH_OPERATION);
+ request.op = crm_element_value_copy(request.xml, PCMK__XA_ST_OP);
CRM_CHECK(request.op != NULL, return);
if (pcmk_is_set(request.call_options, st_opt_sync_call)) {
diff --git a/daemons/fenced/fenced_history.c b/daemons/fenced/fenced_history.c
index a766477..5fcdb1f 100644
--- a/daemons/fenced/fenced_history.c
+++ b/daemons/fenced/fenced_history.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2022 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,6 @@
#include <stdlib.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
@@ -42,23 +41,22 @@ stonith_send_broadcast_history(xmlNode *history,
int callopts,
const char *target)
{
- xmlNode *bcast = create_xml_node(NULL, "stonith_command");
- xmlNode *data = create_xml_node(NULL, __func__);
-
- if (target) {
- crm_xml_add(data, F_STONITH_TARGET, target);
- }
- crm_xml_add(bcast, F_TYPE, T_STONITH_NG);
- crm_xml_add(bcast, F_SUBTYPE, "broadcast");
- crm_xml_add(bcast, F_STONITH_OPERATION, STONITH_OP_FENCE_HISTORY);
- crm_xml_add_int(bcast, F_STONITH_CALLOPTS, callopts);
- if (history) {
- add_node_copy(data, history);
+ xmlNode *bcast = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
+ xmlNode *wrapper = pcmk__xe_create(bcast, PCMK__XE_ST_CALLDATA);
+ xmlNode *call_data = pcmk__xe_create(wrapper, __func__);
+
+ crm_xml_add(bcast, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
+ crm_xml_add(bcast, PCMK__XA_SUBT, PCMK__VALUE_BROADCAST);
+ crm_xml_add(bcast, PCMK__XA_ST_OP, STONITH_OP_FENCE_HISTORY);
+ crm_xml_add_int(bcast, PCMK__XA_ST_CALLOPT, callopts);
+
+ pcmk__xml_copy(call_data, history);
+ if (target != NULL) {
+ crm_xml_add(call_data, PCMK__XA_ST_TARGET, target);
}
- add_message_xml(bcast, F_STONITH_CALLDATA, data);
- send_cluster_message(NULL, crm_msg_stonith_ng, bcast, FALSE);
- free_xml(data);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, bcast);
+
free_xml(bcast);
}
@@ -100,7 +98,7 @@ stonith_fence_history_cleanup(const char *target,
g_hash_table_foreach_remove(stonith_remote_op_list,
stonith_remove_history_entry,
(gpointer) target);
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
}
}
@@ -133,34 +131,68 @@ stonith_fence_history_cleanup(const char *target,
* situations where it would be handy to have it probably.
*/
-
-static int
-op_time_sort(const void *a_voidp, const void *b_voidp)
+/*!
+ * \internal
+ * \brief Compare two remote fencing operations by status and completion time
+ *
+ * A pending operation is ordered before a completed operation. If both
+ * operations have completed, then the more recently completed operation is
+ * ordered first. Two pending operations are considered equal.
+ *
+ * \param[in] a First \c remote_fencing_op_t to compare
+ * \param[in] b Second \c remote_fencing_op_t to compare
+ *
+ * \return Standard comparison result (a negative integer if \p a is lesser,
+ * 0 if the values are equal, and a positive integer if \p a is greater)
+ */
+static gint
+cmp_op_by_completion(gconstpointer a, gconstpointer b)
{
- const remote_fencing_op_t **a = (const remote_fencing_op_t **) a_voidp;
- const remote_fencing_op_t **b = (const remote_fencing_op_t **) b_voidp;
- gboolean a_pending = ((*a)->state != st_failed) && ((*a)->state != st_done);
- gboolean b_pending = ((*b)->state != st_failed) && ((*b)->state != st_done);
+ const remote_fencing_op_t *op1 = a;
+ const remote_fencing_op_t *op2 = b;
+ bool op1_pending = stonith__op_state_pending(op1->state);
+ bool op2_pending = stonith__op_state_pending(op2->state);
- if (a_pending && b_pending) {
+ if (op1_pending && op2_pending) {
return 0;
- } else if (a_pending) {
+ }
+ if (op1_pending) {
return -1;
- } else if (b_pending) {
+ }
+ if (op2_pending) {
return 1;
- } else if ((*b)->completed == (*a)->completed) {
- if ((*b)->completed_nsec > (*a)->completed_nsec) {
- return 1;
- } else if ((*b)->completed_nsec == (*a)->completed_nsec) {
- return 0;
- }
- } else if ((*b)->completed > (*a)->completed) {
+ }
+ if (op1->completed > op2->completed) {
+ return -1;
+ }
+ if (op1->completed < op2->completed) {
return 1;
}
-
- return -1;
+ if (op1->completed_nsec > op2->completed_nsec) {
+ return -1;
+ }
+ if (op1->completed_nsec < op2->completed_nsec) {
+ return 1;
+ }
+ return 0;
}
+/*!
+ * \internal
+ * \brief Remove a completed operation from \c stonith_remote_op_list
+ *
+ * \param[in] data \c remote_fencing_op_t to remove
+ * \param[in] user_data Ignored
+ */
+static void
+remove_completed_remote_op(gpointer data, gpointer user_data)
+{
+ const remote_fencing_op_t *op = data;
+
+ if (!stonith__op_state_pending(op->state)) {
+ g_hash_table_remove(stonith_remote_op_list, op->id);
+ }
+}
/*!
* \internal
@@ -170,43 +202,24 @@ op_time_sort(const void *a_voidp, const void *b_voidp)
void
stonith_fence_history_trim(void)
{
- guint num_ops;
-
- if (!stonith_remote_op_list) {
+ if (stonith_remote_op_list == NULL) {
return;
}
- num_ops = g_hash_table_size(stonith_remote_op_list);
- if (num_ops > MAX_STONITH_HISTORY) {
- remote_fencing_op_t *ops[num_ops];
- remote_fencing_op_t *op = NULL;
- GHashTableIter iter;
- int i;
- crm_trace("Fencing History growing beyond limit of %d so purge "
- "half of failed/successful attempts", MAX_STONITH_HISTORY);
+ if (g_hash_table_size(stonith_remote_op_list) > MAX_STONITH_HISTORY) {
+ GList *ops = g_hash_table_get_values(stonith_remote_op_list);
- /* write all ops into an array */
- i = 0;
- g_hash_table_iter_init(&iter, stonith_remote_op_list);
- while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
- ops[i++] = op;
- }
- /* run quicksort over the array so that we get pending ops
- * first and then sorted most recent to oldest
- */
- qsort(ops, num_ops, sizeof(remote_fencing_op_t *), op_time_sort);
- /* purgest oldest half of the history entries */
- for (i = MAX_STONITH_HISTORY / 2; i < num_ops; i++) {
- /* keep pending ops even if they shouldn't fill more than
- * half of our buffer
- */
- if ((ops[i]->state == st_failed) || (ops[i]->state == st_done)) {
- g_hash_table_remove(stonith_remote_op_list, ops[i]->id);
- }
- }
- /* we've just purged valid data from the list so there is no need
- * to create a notification - if displayed it can stay
- */
+ crm_trace("More than %d entries in fencing history, purging oldest "
+ "completed operations", MAX_STONITH_HISTORY);
+
+ ops = g_list_sort(ops, cmp_op_by_completion);
+
+ // Always keep pending ops regardless of number of entries
+ g_list_foreach(g_list_nth(ops, MAX_STONITH_HISTORY / 2),
+ remove_completed_remote_op, NULL);
+
+ // No need for a notification after purging old data
+ g_list_free(ops);
}
}
@@ -228,10 +241,11 @@ stonith_xml_history_to_list(const xmlNode *history)
CRM_LOG_ASSERT(rv != NULL);
- for (xml_op = pcmk__xml_first_child(history); xml_op != NULL;
- xml_op = pcmk__xml_next(xml_op)) {
+ for (xml_op = pcmk__xe_first_child(history, NULL, NULL, NULL);
+ xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) {
+
remote_fencing_op_t *op = NULL;
- char *id = crm_element_value_copy(xml_op, F_STONITH_REMOTE_OP_ID);
+ char *id = crm_element_value_copy(xml_op, PCMK__XA_ST_REMOTE_OP);
int state;
int exit_status = CRM_EX_OK;
int execution_status = PCMK_EXEC_DONE;
@@ -245,35 +259,37 @@ stonith_xml_history_to_list(const xmlNode *history)
crm_trace("Attaching op %s to hashtable", id);
- op = calloc(1, sizeof(remote_fencing_op_t));
+ op = pcmk__assert_alloc(1, sizeof(remote_fencing_op_t));
op->id = id;
- op->target = crm_element_value_copy(xml_op, F_STONITH_TARGET);
- op->action = crm_element_value_copy(xml_op, F_STONITH_ACTION);
- op->originator = crm_element_value_copy(xml_op, F_STONITH_ORIGIN);
- op->delegate = crm_element_value_copy(xml_op, F_STONITH_DELEGATE);
- op->client_name = crm_element_value_copy(xml_op, F_STONITH_CLIENTNAME);
- crm_element_value_ll(xml_op, F_STONITH_DATE, &completed);
+ op->target = crm_element_value_copy(xml_op, PCMK__XA_ST_TARGET);
+ op->action = crm_element_value_copy(xml_op, PCMK__XA_ST_DEVICE_ACTION);
+ op->originator = crm_element_value_copy(xml_op, PCMK__XA_ST_ORIGIN);
+ op->delegate = crm_element_value_copy(xml_op, PCMK__XA_ST_DELEGATE);
+ op->client_name = crm_element_value_copy(xml_op,
+ PCMK__XA_ST_CLIENTNAME);
+ crm_element_value_ll(xml_op, PCMK__XA_ST_DATE, &completed);
op->completed = (time_t) completed;
- crm_element_value_ll(xml_op, F_STONITH_DATE_NSEC, &completed_nsec);
+ crm_element_value_ll(xml_op, PCMK__XA_ST_DATE_NSEC, &completed_nsec);
op->completed_nsec = completed_nsec;
- crm_element_value_int(xml_op, F_STONITH_STATE, &state);
+ crm_element_value_int(xml_op, PCMK__XA_ST_STATE, &state);
op->state = (enum op_state) state;
/* @COMPAT We can't use stonith__xe_get_result() here because
* fencers <2.1.3 didn't include results, leading it to assume an error
* status. Instead, set an unknown status in that case.
*/
- if ((crm_element_value_int(xml_op, XML_LRM_ATTR_RC, &exit_status) < 0)
- || (crm_element_value_int(xml_op, XML_LRM_ATTR_OPSTATUS,
+ if ((crm_element_value_int(xml_op, PCMK__XA_RC_CODE, &exit_status) < 0)
+ || (crm_element_value_int(xml_op, PCMK__XA_OP_STATUS,
&execution_status) < 0)) {
exit_status = CRM_EX_INDETERMINATE;
execution_status = PCMK_EXEC_UNKNOWN;
}
pcmk__set_result(&op->result, exit_status, execution_status,
- crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON));
+ crm_element_value(xml_op, PCMK_XA_EXIT_REASON));
pcmk__set_result_output(&op->result,
- crm_element_value_copy(xml_op, F_STONITH_OUTPUT),
+ crm_element_value_copy(xml_op,
+ PCMK__XA_ST_OUTPUT),
NULL);
@@ -309,7 +325,7 @@ stonith_local_history_diff_and_merge(GHashTable *remote_history,
if (stonith_remote_op_list) {
char *id = NULL;
- history = create_xml_node(NULL, F_STONITH_HISTORY_LIST);
+ history = pcmk__xe_create(NULL, PCMK__XE_ST_HISTORY);
g_hash_table_iter_init(&iter, stonith_remote_op_list);
while (g_hash_table_iter_next(&iter, (void **)&id, (void **)&op)) {
@@ -361,18 +377,19 @@ stonith_local_history_diff_and_merge(GHashTable *remote_history,
cnt++;
crm_trace("Attaching op %s", op->id);
- entry = create_xml_node(history, STONITH_OP_EXEC);
+ entry = pcmk__xe_create(history, STONITH_OP_EXEC);
if (add_id) {
- crm_xml_add(entry, F_STONITH_REMOTE_OP_ID, op->id);
+ crm_xml_add(entry, PCMK__XA_ST_REMOTE_OP, op->id);
}
- crm_xml_add(entry, F_STONITH_TARGET, op->target);
- crm_xml_add(entry, F_STONITH_ACTION, op->action);
- crm_xml_add(entry, F_STONITH_ORIGIN, op->originator);
- crm_xml_add(entry, F_STONITH_DELEGATE, op->delegate);
- crm_xml_add(entry, F_STONITH_CLIENTNAME, op->client_name);
- crm_xml_add_ll(entry, F_STONITH_DATE, op->completed);
- crm_xml_add_ll(entry, F_STONITH_DATE_NSEC, op->completed_nsec);
- crm_xml_add_int(entry, F_STONITH_STATE, op->state);
+ crm_xml_add(entry, PCMK__XA_ST_TARGET, op->target);
+ crm_xml_add(entry, PCMK__XA_ST_DEVICE_ACTION, op->action);
+ crm_xml_add(entry, PCMK__XA_ST_ORIGIN, op->originator);
+ crm_xml_add(entry, PCMK__XA_ST_DELEGATE, op->delegate);
+ crm_xml_add(entry, PCMK__XA_ST_CLIENTNAME, op->client_name);
+ crm_xml_add_ll(entry, PCMK__XA_ST_DATE, op->completed);
+ crm_xml_add_ll(entry, PCMK__XA_ST_DATE_NSEC,
+ op->completed_nsec);
+ crm_xml_add_int(entry, PCMK__XA_ST_STATE, op->state);
stonith__xe_set_result(entry, &op->result);
}
}
@@ -418,7 +435,7 @@ stonith_local_history_diff_and_merge(GHashTable *remote_history,
if (updated) {
stonith_fence_history_trim();
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
}
if (cnt == 0) {
@@ -459,17 +476,19 @@ stonith_fence_history(xmlNode *msg, xmlNode **output,
const char *remote_peer, int options)
{
const char *target = NULL;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_NEVER);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, msg, LOG_NEVER);
xmlNode *out_history = NULL;
if (dev) {
- target = crm_element_value(dev, F_STONITH_TARGET);
+ target = crm_element_value(dev, PCMK__XA_ST_TARGET);
if (target && (options & st_opt_cs_nodeid)) {
int nodeid;
crm_node_t *node;
pcmk__scan_min_int(target, &nodeid, 0);
- node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
+ node = pcmk__search_node_caches(nodeid, NULL,
+ pcmk__node_search_any
+ |pcmk__node_search_cluster_cib);
if (node) {
target = node->uname;
}
@@ -477,18 +496,20 @@ stonith_fence_history(xmlNode *msg, xmlNode **output,
}
if (options & st_opt_cleanup) {
+ const char *call_id = crm_element_value(msg, PCMK__XA_ST_CALLID);
+
crm_trace("Cleaning up operations on %s in %p", target,
stonith_remote_op_list);
+ stonith_fence_history_cleanup(target, (call_id != NULL));
- stonith_fence_history_cleanup(target,
- crm_element_value(msg, F_STONITH_CALLID) != NULL);
} else if (options & st_opt_broadcast) {
/* there is no clear sign atm for when a history sync
is done so send a notification for anything
that smells like history-sync
*/
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY_SYNCED, NULL, NULL);
- if (crm_element_value(msg, F_STONITH_CALLID)) {
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED, NULL,
+ NULL);
+ if (crm_element_value(msg, PCMK__XA_ST_CALLID) != NULL) {
/* this is coming from the stonith-API
*
* craft a broadcast with node's history
@@ -502,8 +523,8 @@ stonith_fence_history(xmlNode *msg, xmlNode **output,
NULL);
} else if (remote_peer &&
!pcmk__str_eq(remote_peer, stonith_our_uname, pcmk__str_casei)) {
- xmlNode *history = get_xpath_object("//" F_STONITH_HISTORY_LIST,
- msg, LOG_NEVER);
+ xmlNode *history = get_xpath_object("//" PCMK__XE_ST_HISTORY, msg,
+ LOG_NEVER);
/* either a broadcast created directly upon stonith-API request
* or a diff as response to such a thing
@@ -514,7 +535,9 @@ stonith_fence_history(xmlNode *msg, xmlNode **output,
* otherwise broadcast what we have on top
* marking as differential and merge in afterwards
*/
- if (!history || !pcmk__xe_attr_is_true(history, F_STONITH_DIFFERENTIAL)) {
+ if (!history
+ || !pcmk__xe_attr_is_true(history, PCMK__XA_ST_DIFFERENTIAL)) {
+
GHashTable *received_history = NULL;
if (history != NULL) {
@@ -524,7 +547,8 @@ stonith_fence_history(xmlNode *msg, xmlNode **output,
stonith_local_history_diff_and_merge(received_history, TRUE, NULL);
if (out_history) {
crm_trace("Broadcasting history-diff to peers");
- pcmk__xe_set_bool_attr(out_history, F_STONITH_DIFFERENTIAL, true);
+ pcmk__xe_set_bool_attr(out_history,
+ PCMK__XA_ST_DIFFERENTIAL, true);
stonith_send_broadcast_history(out_history,
st_opt_broadcast | st_opt_discard_reply,
NULL);
diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c
index 843b3d4..f87eeb6 100644
--- a/daemons/fenced/fenced_remote.c
+++ b/daemons/fenced/fenced_remote.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.
*
@@ -24,7 +24,6 @@
#include <regex.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
#include <crm/cluster/internal.h>
@@ -370,24 +369,25 @@ undo_op_remap(remote_fencing_op_t *op)
* \internal
* \brief Create notification data XML for a fencing operation result
*
- * \param[in] op Fencer operation that completed
+ * \param[in,out] parent Parent XML element for newly created element
+ * \param[in] op Fencer operation that completed
*
* \return Newly created XML to add as notification data
* \note The caller is responsible for freeing the result.
*/
static xmlNode *
-fencing_result2xml(const remote_fencing_op_t *op)
+fencing_result2xml(xmlNode *parent, const remote_fencing_op_t *op)
{
- xmlNode *notify_data = create_xml_node(NULL, T_STONITH_NOTIFY_FENCE);
+ xmlNode *notify_data = pcmk__xe_create(parent, PCMK__XE_ST_NOTIFY_FENCE);
- crm_xml_add_int(notify_data, "state", op->state);
- crm_xml_add(notify_data, F_STONITH_TARGET, op->target);
- crm_xml_add(notify_data, F_STONITH_ACTION, op->action);
- crm_xml_add(notify_data, F_STONITH_DELEGATE, op->delegate);
- crm_xml_add(notify_data, F_STONITH_REMOTE_OP_ID, op->id);
- crm_xml_add(notify_data, F_STONITH_ORIGIN, op->originator);
- crm_xml_add(notify_data, F_STONITH_CLIENTID, op->client_id);
- crm_xml_add(notify_data, F_STONITH_CLIENTNAME, op->client_name);
+ crm_xml_add_int(notify_data, PCMK_XA_STATE, op->state);
+ crm_xml_add(notify_data, PCMK__XA_ST_TARGET, op->target);
+ crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ACTION, op->action);
+ crm_xml_add(notify_data, PCMK__XA_ST_DELEGATE, op->delegate);
+ crm_xml_add(notify_data, PCMK__XA_ST_REMOTE_OP, op->id);
+ crm_xml_add(notify_data, PCMK__XA_ST_ORIGIN, op->originator);
+ crm_xml_add(notify_data, PCMK__XA_ST_CLIENTID, op->client_id);
+ crm_xml_add(notify_data, PCMK__XA_ST_CLIENTNAME, op->client_name);
return notify_data;
}
@@ -403,25 +403,26 @@ void
fenced_broadcast_op_result(const remote_fencing_op_t *op, bool op_merged)
{
static int count = 0;
- xmlNode *bcast = create_xml_node(NULL, T_STONITH_REPLY);
- xmlNode *notify_data = fencing_result2xml(op);
+ xmlNode *bcast = pcmk__xe_create(NULL, PCMK__XE_ST_REPLY);
+ xmlNode *wrapper = NULL;
+ xmlNode *notify_data = NULL;
count++;
crm_trace("Broadcasting result to peers");
- crm_xml_add(bcast, F_TYPE, T_STONITH_NOTIFY);
- crm_xml_add(bcast, F_SUBTYPE, "broadcast");
- crm_xml_add(bcast, F_STONITH_OPERATION, T_STONITH_NOTIFY);
- crm_xml_add_int(bcast, "count", count);
+ crm_xml_add(bcast, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
+ crm_xml_add(bcast, PCMK__XA_SUBT, PCMK__VALUE_BROADCAST);
+ crm_xml_add(bcast, PCMK__XA_ST_OP, STONITH_OP_NOTIFY);
+ crm_xml_add_int(bcast, PCMK_XA_COUNT, count);
if (op_merged) {
- pcmk__xe_set_bool_attr(bcast, F_STONITH_MERGED, true);
+ pcmk__xe_set_bool_attr(bcast, PCMK__XA_ST_OP_MERGED, true);
}
+ wrapper = pcmk__xe_create(bcast, PCMK__XE_ST_CALLDATA);
+ notify_data = fencing_result2xml(wrapper, op);
stonith__xe_set_result(notify_data, &op->result);
- add_message_xml(bcast, F_STONITH_CALLDATA, notify_data);
- send_cluster_message(NULL, crm_msg_stonith_ng, bcast, FALSE);
- free_xml(notify_data);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, bcast);
free_xml(bcast);
return;
@@ -447,12 +448,12 @@ handle_local_reply_and_notify(remote_fencing_op_t *op, xmlNode *data)
}
/* Do notification with a clean data object */
- crm_xml_add_int(data, "state", op->state);
- crm_xml_add(data, F_STONITH_TARGET, op->target);
- crm_xml_add(data, F_STONITH_OPERATION, op->action);
+ crm_xml_add_int(data, PCMK_XA_STATE, op->state);
+ crm_xml_add(data, PCMK__XA_ST_TARGET, op->target);
+ crm_xml_add(data, PCMK__XA_ST_OP, op->action);
reply = fenced_construct_reply(op->request, data, &op->result);
- crm_xml_add(reply, F_STONITH_DELEGATE, op->delegate);
+ crm_xml_add(reply, PCMK__XA_ST_DELEGATE, op->delegate);
/* Send fencing OP reply to local client that initiated fencing */
client = pcmk__find_client_by_id(op->client_id);
@@ -463,10 +464,11 @@ handle_local_reply_and_notify(remote_fencing_op_t *op, xmlNode *data)
}
/* bcast to all local clients that the fencing operation happend */
- notify_data = fencing_result2xml(op);
- fenced_send_notification(T_STONITH_NOTIFY_FENCE, &op->result, notify_data);
+ notify_data = fencing_result2xml(NULL, op);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_FENCE, &op->result,
+ notify_data);
free_xml(notify_data);
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
/* mark this op as having notify's already sent */
op->notify_sent = TRUE;
@@ -509,12 +511,13 @@ finalize_op_duplicates(remote_fencing_op_t *op, xmlNode *data)
static char *
delegate_from_xml(xmlNode *xml)
{
- xmlNode *match = get_xpath_object("//@" F_STONITH_DELEGATE, xml, LOG_NEVER);
+ xmlNode *match = get_xpath_object("//@" PCMK__XA_ST_DELEGATE, xml,
+ LOG_NEVER);
if (match == NULL) {
- return crm_element_value_copy(xml, F_ORIG);
+ return crm_element_value_copy(xml, PCMK__XA_SRC);
} else {
- return crm_element_value_copy(match, F_STONITH_DELEGATE);
+ return crm_element_value_copy(match, PCMK__XA_ST_DELEGATE);
}
}
@@ -564,7 +567,7 @@ finalize_op(remote_fencing_op_t *op, xmlNode *data, bool dup)
undo_op_remap(op);
if (data == NULL) {
- data = create_xml_node(NULL, "remote-op");
+ data = pcmk__xe_create(NULL, "remote-op");
local_data = data;
} else if (op->delegate == NULL) {
@@ -584,15 +587,15 @@ finalize_op(remote_fencing_op_t *op, xmlNode *data, bool dup)
}
}
- if (dup || (crm_element_value(data, F_STONITH_MERGED) != NULL)) {
+ if (dup || (crm_element_value(data, PCMK__XA_ST_OP_MERGED) != NULL)) {
op_merged = true;
}
/* Tell everyone the operation is done, we will continue
* with doing the local notifications once we receive
* the broadcast back. */
- subt = crm_element_value(data, F_SUBTYPE);
- if (!dup && !pcmk__str_eq(subt, "broadcast", pcmk__str_casei)) {
+ subt = crm_element_value(data, PCMK__XA_SUBT);
+ if (!dup && !pcmk__str_eq(subt, PCMK__VALUE_BROADCAST, pcmk__str_none)) {
/* Defer notification until the bcast message arrives */
fenced_broadcast_op_result(op, op_merged);
free_xml(local_data);
@@ -800,7 +803,8 @@ add_required_device(remote_fencing_op_t *op, const char *device)
sort_strings);
if (!match) {
- op->automatic_list = g_list_prepend(op->automatic_list, strdup(device));
+ op->automatic_list = g_list_prepend(op->automatic_list,
+ pcmk__str_copy(device));
}
}
@@ -833,7 +837,10 @@ set_op_device_list(remote_fencing_op_t * op, GList *devices)
op->devices_list = NULL;
}
for (lpc = devices; lpc != NULL; lpc = lpc->next) {
- op->devices_list = g_list_append(op->devices_list, strdup(lpc->data));
+ const char *device = lpc->data;
+
+ op->devices_list = g_list_append(op->devices_list,
+ pcmk__str_copy(device));
}
op->devices = op->devices_list;
}
@@ -1001,6 +1008,7 @@ merge_duplicates(remote_fencing_op_t *op)
g_hash_table_iter_init(&iter, stonith_remote_op_list);
while (g_hash_table_iter_next(&iter, NULL, (void **)&other)) {
const char *other_action = op_requested_action(other);
+ crm_node_t *node = NULL;
if (!strcmp(op->id, other->id)) {
continue; // Don't compare against self
@@ -1030,7 +1038,11 @@ merge_duplicates(remote_fencing_op_t *op)
op->id, other->id, other->target);
continue;
}
- if (!fencing_peer_active(crm_get_peer(0, other->originator))) {
+
+ node = pcmk__get_node(0, other->originator, NULL,
+ pcmk__node_search_cluster_member);
+
+ if (!fencing_peer_active(node)) {
crm_notice("Failing action '%s' targeting %s originating from "
"client %s@%s: Originator is dead " CRM_XS " id=%.8s",
other->action, other->target, other->client_name,
@@ -1042,8 +1054,8 @@ merge_duplicates(remote_fencing_op_t *op)
}
if ((other->total_timeout > 0)
&& (now > (other->total_timeout + other->created))) {
- crm_trace("%.8s not duplicate of %.8s: old (%ld vs. %ld + %d)",
- op->id, other->id, now, other->created,
+ crm_trace("%.8s not duplicate of %.8s: old (%lld vs. %lld + %ds)",
+ op->id, other->id, (long long)now, (long long)other->created,
other->total_timeout);
continue;
}
@@ -1055,7 +1067,7 @@ merge_duplicates(remote_fencing_op_t *op)
if (other->total_timeout == 0) {
other->total_timeout = op->total_timeout =
TIMEOUT_MULTIPLY_FACTOR * get_op_total_timeout(op, NULL);
- crm_trace("Best guess as to timeout used for %.8s: %d",
+ crm_trace("Best guess as to timeout used for %.8s: %ds",
other->id, other->total_timeout);
}
crm_notice("Merging fencing action '%s' targeting %s originating from "
@@ -1097,12 +1109,12 @@ int
fenced_handle_manual_confirmation(const pcmk__client_t *client, xmlNode *msg)
{
remote_fencing_op_t *op = NULL;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_ERR);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, msg, LOG_ERR);
CRM_CHECK(dev != NULL, return EPROTO);
crm_notice("Received manual confirmation that %s has been fenced",
- pcmk__s(crm_element_value(dev, F_STONITH_TARGET),
+ pcmk__s(crm_element_value(dev, PCMK__XA_ST_TARGET),
"unknown target"));
op = initiate_remote_stonith_op(client, msg, TRUE);
if (op == NULL) {
@@ -1110,7 +1122,7 @@ fenced_handle_manual_confirmation(const pcmk__client_t *client, xmlNode *msg)
}
op->state = st_done;
set_fencing_completed(op);
- op->delegate = strdup("a human");
+ op->delegate = pcmk__str_copy("a human");
// For the fencer's purposes, the fencing operation is done
pcmk__set_result(&op->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
@@ -1137,7 +1149,8 @@ void *
create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
{
remote_fencing_op_t *op = NULL;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, request, LOG_NEVER);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_TARGET, request,
+ LOG_NEVER);
int call_options = 0;
const char *operation = NULL;
@@ -1146,7 +1159,7 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
/* If this operation is owned by another node, check to make
* sure we haven't already created this operation. */
if (peer && dev) {
- const char *op_id = crm_element_value(dev, F_STONITH_REMOTE_OP_ID);
+ const char *op_id = crm_element_value(dev, PCMK__XA_ST_REMOTE_OP);
CRM_CHECK(op_id != NULL, return NULL);
@@ -1158,15 +1171,14 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
}
}
- op = calloc(1, sizeof(remote_fencing_op_t));
- CRM_ASSERT(op != NULL);
+ op = pcmk__assert_alloc(1, sizeof(remote_fencing_op_t));
- crm_element_value_int(request, F_STONITH_TIMEOUT, &(op->base_timeout));
+ crm_element_value_int(request, PCMK__XA_ST_TIMEOUT, &(op->base_timeout));
// Value -1 means disable any static/random fencing delays
- crm_element_value_int(request, F_STONITH_DELAY, &(op->client_delay));
+ crm_element_value_int(request, PCMK__XA_ST_DELAY, &(op->client_delay));
if (peer && dev) {
- op->id = crm_element_value_copy(dev, F_STONITH_REMOTE_OP_ID);
+ op->id = crm_element_value_copy(dev, PCMK__XA_ST_REMOTE_OP);
} else {
op->id = crm_generate_uuid();
}
@@ -1175,41 +1187,49 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
op->state = st_query;
op->replies_expected = fencing_active_peers();
- op->action = crm_element_value_copy(dev, F_STONITH_ACTION);
- op->originator = crm_element_value_copy(dev, F_STONITH_ORIGIN);
- op->delegate = crm_element_value_copy(dev, F_STONITH_DELEGATE); /* May not be set */
- op->created = time(NULL);
+ op->action = crm_element_value_copy(dev, PCMK__XA_ST_DEVICE_ACTION);
+ /* The node initiating the stonith operation. If an operation is relayed,
+ * this is the last node the operation lands on. When in standalone mode,
+ * origin is the ID of the client that originated the operation.
+ *
+ * Or may be the name of the function that created the operation.
+ */
+ op->originator = crm_element_value_copy(dev, PCMK__XA_ST_ORIGIN);
if (op->originator == NULL) {
/* Local or relayed request */
- op->originator = strdup(stonith_our_uname);
+ op->originator = pcmk__str_copy(stonith_our_uname);
}
- CRM_LOG_ASSERT(client != NULL);
- if (client) {
- op->client_id = strdup(client);
- }
+ // Delegate may not be set
+ op->delegate = crm_element_value_copy(dev, PCMK__XA_ST_DELEGATE);
+ op->created = time(NULL);
+ CRM_LOG_ASSERT(client != NULL);
+ op->client_id = pcmk__str_copy(client);
/* For a RELAY operation, set fenced on the client. */
- operation = crm_element_value(request, F_STONITH_OPERATION);
+ operation = crm_element_value(request, PCMK__XA_ST_OP);
if (pcmk__str_eq(operation, STONITH_OP_RELAY, pcmk__str_none)) {
op->client_name = crm_strdup_printf("%s.%lu", crm_system_name,
(unsigned long) getpid());
} else {
- op->client_name = crm_element_value_copy(request, F_STONITH_CLIENTNAME);
+ op->client_name = crm_element_value_copy(request,
+ PCMK__XA_ST_CLIENTNAME);
}
- op->target = crm_element_value_copy(dev, F_STONITH_TARGET);
- op->request = copy_xml(request); /* TODO: Figure out how to avoid this */
- crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options);
+ op->target = crm_element_value_copy(dev, PCMK__XA_ST_TARGET);
+
+ // @TODO Figure out how to avoid copying XML here
+ op->request = pcmk__xml_copy(NULL, request);
+ crm_element_value_int(request, PCMK__XA_ST_CALLOPT, &call_options);
op->call_options = call_options;
- crm_element_value_int(request, F_STONITH_CALLID, &(op->client_callid));
+ crm_element_value_int(request, PCMK__XA_ST_CALLID, &(op->client_callid));
crm_trace("%s new fencing op %s ('%s' targeting %s for client %s, "
- "base timeout %d, %u %s expected)",
+ "base timeout %ds, %u %s expected)",
(peer && dev)? "Recorded" : "Generated", op->id, op->action,
op->target, op->client_name, op->base_timeout,
op->replies_expected,
@@ -1220,14 +1240,15 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
crm_node_t *node;
pcmk__scan_min_int(op->target, &nodeid, 0);
- node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
+ node = pcmk__search_node_caches(nodeid, NULL,
+ pcmk__node_search_any
+ |pcmk__node_search_cluster_cib);
/* Ensure the conversion only happens once */
stonith__clear_call_options(op->call_options, op->id, st_opt_cs_nodeid);
if (node && node->uname) {
- free(op->target);
- op->target = strdup(node->uname);
+ pcmk__str_update(&(op->target), node->uname);
} else {
crm_warn("Could not expand nodeid '%s' into a host name", op->target);
@@ -1239,7 +1260,7 @@ create_remote_stonith_op(const char *client, xmlNode *request, gboolean peer)
if (op->state != st_duplicate) {
/* kick history readers */
- fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
+ fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
}
/* safe to trim as long as that doesn't touch pending ops */
@@ -1272,7 +1293,7 @@ initiate_remote_stonith_op(const pcmk__client_t *client, xmlNode *request,
if (client) {
client_id = client->id;
} else {
- client_id = crm_element_value(request, F_STONITH_CLIENTID);
+ client_id = crm_element_value(request, PCMK__XA_ST_CLIENTID);
}
CRM_LOG_ASSERT(client_id != NULL);
@@ -1305,7 +1326,7 @@ initiate_remote_stonith_op(const pcmk__client_t *client, xmlNode *request,
default:
crm_notice("Requesting peer fencing (%s) targeting %s "
- CRM_XS " id=%.8s state=%s base_timeout=%d",
+ CRM_XS " id=%.8s state=%s base_timeout=%ds",
op->action, op->target, op->id,
stonith_op_state_str(op->state), op->base_timeout);
}
@@ -1313,24 +1334,24 @@ initiate_remote_stonith_op(const pcmk__client_t *client, xmlNode *request,
query = stonith_create_op(op->client_callid, op->id, STONITH_OP_QUERY,
NULL, op->call_options);
- crm_xml_add(query, F_STONITH_REMOTE_OP_ID, op->id);
- crm_xml_add(query, F_STONITH_TARGET, op->target);
- crm_xml_add(query, F_STONITH_ACTION, op_requested_action(op));
- crm_xml_add(query, F_STONITH_ORIGIN, op->originator);
- crm_xml_add(query, F_STONITH_CLIENTID, op->client_id);
- crm_xml_add(query, F_STONITH_CLIENTNAME, op->client_name);
- crm_xml_add_int(query, F_STONITH_TIMEOUT, op->base_timeout);
+ crm_xml_add(query, PCMK__XA_ST_REMOTE_OP, op->id);
+ crm_xml_add(query, PCMK__XA_ST_TARGET, op->target);
+ crm_xml_add(query, PCMK__XA_ST_DEVICE_ACTION, op_requested_action(op));
+ crm_xml_add(query, PCMK__XA_ST_ORIGIN, op->originator);
+ crm_xml_add(query, PCMK__XA_ST_CLIENTID, op->client_id);
+ crm_xml_add(query, PCMK__XA_ST_CLIENTNAME, op->client_name);
+ crm_xml_add_int(query, PCMK__XA_ST_TIMEOUT, op->base_timeout);
/* In case of RELAY operation, RELAY information is added to the query to delete the original operation of RELAY. */
- operation = crm_element_value(request, F_STONITH_OPERATION);
+ operation = crm_element_value(request, PCMK__XA_ST_OP);
if (pcmk__str_eq(operation, STONITH_OP_RELAY, pcmk__str_none)) {
- relay_op_id = crm_element_value(request, F_STONITH_REMOTE_OP_ID);
+ relay_op_id = crm_element_value(request, PCMK__XA_ST_REMOTE_OP);
if (relay_op_id) {
- crm_xml_add(query, F_STONITH_REMOTE_OP_ID_RELAY, relay_op_id);
+ crm_xml_add(query, PCMK__XA_ST_REMOTE_OP_RELAY, relay_op_id);
}
}
- send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, query);
free_xml(query);
query_timeout = op->base_timeout * TIMEOUT_MULTIPLY_FACTOR;
@@ -1348,6 +1369,16 @@ enum find_best_peer_options {
FIND_PEER_VERIFIED_ONLY = 0x0004,
};
+static bool
+is_watchdog_fencing(const remote_fencing_op_t *op, const char *device)
+{
+ return (stonith_watchdog_timeout_ms > 0
+ // Only an explicit mismatch is considered not a watchdog fencing.
+ && pcmk__str_eq(device, STONITH_WATCHDOG_ID, pcmk__str_null_matches)
+ && pcmk__is_fencing_action(op->action)
+ && node_does_watchdog_fencing(op->target));
+}
+
static peer_device_info_t *
find_best_peer(const char *device, remote_fencing_op_t * op, enum find_best_peer_options options)
{
@@ -1443,10 +1474,10 @@ stonith_choose_peer(remote_fencing_op_t * op)
&& pcmk_is_set(op->call_options, st_opt_topology)
&& (advance_topology_level(op, false) == pcmk_rc_ok));
- if ((stonith_watchdog_timeout_ms > 0)
- && pcmk__is_fencing_action(op->action)
- && pcmk__str_eq(device, STONITH_WATCHDOG_ID, pcmk__str_none)
- && node_does_watchdog_fencing(op->target)) {
+ /* With a simple watchdog fencing configuration without a topology,
+ * "device" is NULL here. Consider it should be done with watchdog fencing.
+ */
+ if (is_watchdog_fencing(op, device)) {
crm_info("Couldn't contact watchdog-fencing target-node (%s)",
op->target);
/* check_watchdog_fencing_and_wait will log additional info */
@@ -1458,32 +1489,69 @@ stonith_choose_peer(remote_fencing_op_t * op)
}
static int
+valid_fencing_timeout(int specified_timeout, bool action_specific,
+ const remote_fencing_op_t *op, const char *device)
+{
+ int timeout = specified_timeout;
+
+ if (!is_watchdog_fencing(op, device)) {
+ return timeout;
+ }
+
+ timeout = (int) QB_MIN(QB_MAX(specified_timeout,
+ stonith_watchdog_timeout_ms / 1000), INT_MAX);
+
+ if (timeout > specified_timeout) {
+ if (action_specific) {
+ crm_warn("pcmk_%s_timeout %ds for %s is too short (must be >= "
+ PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " %ds), using %ds "
+ "instead",
+ op->action, specified_timeout, device? device : "watchdog",
+ timeout, timeout);
+
+ } else {
+ crm_warn("Fencing timeout %ds is too short (must be >= "
+ PCMK_OPT_STONITH_WATCHDOG_TIMEOUT " %ds), using %ds "
+ "instead",
+ specified_timeout, timeout, timeout);
+ }
+ }
+
+ return timeout;
+}
+
+static int
get_device_timeout(const remote_fencing_op_t *op,
const peer_device_info_t *peer, const char *device,
bool with_delay)
{
+ int timeout = op->base_timeout;
device_properties_t *props;
- int delay = 0;
+
+ timeout = valid_fencing_timeout(op->base_timeout, false, op, device);
if (!peer || !device) {
- return op->base_timeout;
+ return timeout;
}
props = g_hash_table_lookup(peer->devices, device);
if (!props) {
- return op->base_timeout;
+ return timeout;
+ }
+
+ if (props->custom_action_timeout[op->phase]) {
+ timeout = valid_fencing_timeout(props->custom_action_timeout[op->phase],
+ true, op, device);
}
// op->client_delay < 0 means disable any static/random fencing delays
if (with_delay && (op->client_delay >= 0)) {
// delay_base is eventually limited by delay_max
- delay = (props->delay_max[op->phase] > 0 ?
- props->delay_max[op->phase] : props->delay_base[op->phase]);
+ timeout += (props->delay_max[op->phase] > 0 ?
+ props->delay_max[op->phase] : props->delay_base[op->phase]);
}
- return (props->custom_action_timeout[op->phase]?
- props->custom_action_timeout[op->phase] : op->base_timeout)
- + delay;
+ return timeout;
}
struct timeout_data {
@@ -1532,7 +1600,7 @@ static int
get_op_total_timeout(const remote_fencing_op_t *op,
const peer_device_info_t *chosen_peer)
{
- int total_timeout = 0;
+ long long total_timeout = 0;
stonith_topology_t *tp = find_topology_for_host(op->target);
if (pcmk_is_set(op->call_options, st_opt_topology) && tp) {
@@ -1558,17 +1626,7 @@ get_op_total_timeout(const remote_fencing_op_t *op,
continue;
}
for (device_list = tp->levels[i]; device_list; device_list = device_list->next) {
- /* in case of watchdog-device we add the timeout to the budget
- regardless of if we got a reply or not
- */
- if ((stonith_watchdog_timeout_ms > 0)
- && pcmk__is_fencing_action(op->action)
- && pcmk__str_eq(device_list->data, STONITH_WATCHDOG_ID,
- pcmk__str_none)
- && node_does_watchdog_fencing(op->target)) {
- total_timeout += stonith_watchdog_timeout_ms / 1000;
- continue;
- }
+ bool found = false;
for (iter = op->query_results; iter != NULL; iter = iter->next) {
const peer_device_info_t *peer = iter->data;
@@ -1586,9 +1644,17 @@ get_op_total_timeout(const remote_fencing_op_t *op,
total_timeout += get_device_timeout(op, peer,
device_list->data,
true);
+ found = true;
break;
}
} /* End Loop3: match device with peer that owns device, find device's timeout period */
+
+ /* in case of watchdog-device we add the timeout to the budget
+ if didn't get a reply
+ */
+ if (!found && is_watchdog_fencing(op, device_list->data)) {
+ total_timeout += stonith_watchdog_timeout_ms / 1000;
+ }
} /* End Loop2: iterate through devices at a specific level */
} /*End Loop1: iterate through fencing levels */
@@ -1612,15 +1678,23 @@ get_op_total_timeout(const remote_fencing_op_t *op,
} else if (chosen_peer) {
total_timeout = get_peer_timeout(op, chosen_peer);
+
} else {
+ total_timeout = valid_fencing_timeout(op->base_timeout, false, op,
+ NULL);
+ }
+
+ if (total_timeout <= 0) {
total_timeout = op->base_timeout;
}
/* Take any requested fencing delay into account to prevent it from eating
* up the total timeout.
*/
- return ((total_timeout ? total_timeout : op->base_timeout)
- + ((op->client_delay > 0)? op->client_delay : 0));
+ if (op->client_delay > 0) {
+ total_timeout += op->client_delay;
+ }
+ return (int) QB_MIN(total_timeout, INT_MAX);
}
static void
@@ -1643,9 +1717,9 @@ report_timeout_period(remote_fencing_op_t * op, int op_timeout)
}
crm_trace("Reporting timeout for %s (id=%.8s)", op->client_name, op->id);
- client_node = crm_element_value(op->request, F_STONITH_CLIENTNODE);
- call_id = crm_element_value(op->request, F_STONITH_CALLID);
- client_id = crm_element_value(op->request, F_STONITH_CLIENTID);
+ client_node = crm_element_value(op->request, PCMK__XA_ST_CLIENTNODE);
+ call_id = crm_element_value(op->request, PCMK__XA_ST_CALLID);
+ client_id = crm_element_value(op->request, PCMK__XA_ST_CLIENTID);
if (!client_node || !call_id || !client_id) {
return;
}
@@ -1658,12 +1732,14 @@ report_timeout_period(remote_fencing_op_t * op, int op_timeout)
/* The client is connected to another node, relay this update to them */
update = stonith_create_op(op->client_callid, op->id, STONITH_OP_TIMEOUT_UPDATE, NULL, 0);
- crm_xml_add(update, F_STONITH_REMOTE_OP_ID, op->id);
- crm_xml_add(update, F_STONITH_CLIENTID, client_id);
- crm_xml_add(update, F_STONITH_CALLID, call_id);
- crm_xml_add_int(update, F_STONITH_TIMEOUT, op_timeout);
+ crm_xml_add(update, PCMK__XA_ST_REMOTE_OP, op->id);
+ crm_xml_add(update, PCMK__XA_ST_CLIENTID, client_id);
+ crm_xml_add(update, PCMK__XA_ST_CALLID, call_id);
+ crm_xml_add_int(update, PCMK__XA_ST_TIMEOUT, op_timeout);
- send_cluster_message(crm_get_peer(0, client_node), crm_msg_stonith_ng, update, FALSE);
+ pcmk__cluster_send_message(pcmk__get_node(0, client_node, NULL,
+ pcmk__node_search_cluster_member),
+ crm_msg_stonith_ng, update);
free_xml(update);
@@ -1742,17 +1818,18 @@ static gboolean
check_watchdog_fencing_and_wait(remote_fencing_op_t * op)
{
if (node_does_watchdog_fencing(op->target)) {
+ guint timeout_ms = QB_MIN(stonith_watchdog_timeout_ms, UINT_MAX);
- crm_notice("Waiting %lds for %s to self-fence (%s) for "
+ crm_notice("Waiting %s for %s to self-fence (%s) for "
"client %s " CRM_XS " id=%.8s",
- (stonith_watchdog_timeout_ms / 1000),
- op->target, op->action, op->client_name, op->id);
+ pcmk__readable_interval(timeout_ms), op->target, op->action,
+ op->client_name, op->id);
if (op->op_timer_one) {
g_source_remove(op->op_timer_one);
}
- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms,
- remote_op_watchdog_done, op);
+ op->op_timer_one = g_timeout_add(timeout_ms, remote_op_watchdog_done,
+ op);
return TRUE;
} else {
crm_debug("Skipping fallback to watchdog-fencing as %s is "
@@ -1819,7 +1896,7 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
op->total_timeout = TIMEOUT_MULTIPLY_FACTOR * get_op_total_timeout(op, peer);
op->op_timer_total = g_timeout_add(1000 * op->total_timeout, remote_op_timeout, op);
report_timeout_period(op, op->total_timeout);
- crm_info("Total timeout set to %d for peer's fencing targeting %s for %s"
+ crm_info("Total timeout set to %ds for peer's fencing targeting %s for %s"
CRM_XS "id=%.8s",
op->total_timeout, op->target, op->client_name, op->id);
}
@@ -1846,6 +1923,9 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
if (peer) {
int timeout_one = 0;
xmlNode *remote_op = stonith_create_op(op->client_callid, op->id, STONITH_OP_FENCE, NULL, 0);
+ const crm_node_t *peer_node =
+ pcmk__get_node(0, peer->host, NULL,
+ pcmk__node_search_cluster_member);
if (op->client_delay > 0) {
/* Take requested fencing delay into account to prevent it from
@@ -1854,15 +1934,15 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
timeout_one = TIMEOUT_MULTIPLY_FACTOR * op->client_delay;
}
- crm_xml_add(remote_op, F_STONITH_REMOTE_OP_ID, op->id);
- crm_xml_add(remote_op, F_STONITH_TARGET, op->target);
- crm_xml_add(remote_op, F_STONITH_ACTION, op->action);
- crm_xml_add(remote_op, F_STONITH_ORIGIN, op->originator);
- crm_xml_add(remote_op, F_STONITH_CLIENTID, op->client_id);
- crm_xml_add(remote_op, F_STONITH_CLIENTNAME, op->client_name);
- crm_xml_add_int(remote_op, F_STONITH_TIMEOUT, timeout);
- crm_xml_add_int(remote_op, F_STONITH_CALLOPTS, op->call_options);
- crm_xml_add_int(remote_op, F_STONITH_DELAY, op->client_delay);
+ crm_xml_add(remote_op, PCMK__XA_ST_REMOTE_OP, op->id);
+ crm_xml_add(remote_op, PCMK__XA_ST_TARGET, op->target);
+ crm_xml_add(remote_op, PCMK__XA_ST_DEVICE_ACTION, op->action);
+ crm_xml_add(remote_op, PCMK__XA_ST_ORIGIN, op->originator);
+ crm_xml_add(remote_op, PCMK__XA_ST_CLIENTID, op->client_id);
+ crm_xml_add(remote_op, PCMK__XA_ST_CLIENTNAME, op->client_name);
+ crm_xml_add_int(remote_op, PCMK__XA_ST_TIMEOUT, timeout);
+ crm_xml_add_int(remote_op, PCMK__XA_ST_CALLOPT, op->call_options);
+ crm_xml_add_int(remote_op, PCMK__XA_ST_DELAY, op->client_delay);
if (device) {
timeout_one += TIMEOUT_MULTIPLY_FACTOR *
@@ -1871,14 +1951,15 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
"using %s " CRM_XS " for client %s (%ds)",
peer->host, op->action, op->target, device,
op->client_name, timeout_one);
- crm_xml_add(remote_op, F_STONITH_DEVICE, device);
+ crm_xml_add(remote_op, PCMK__XA_ST_DEVICE_ID, device);
} else {
timeout_one += TIMEOUT_MULTIPLY_FACTOR * get_peer_timeout(op, peer);
crm_notice("Requesting that %s perform '%s' action targeting %s "
- CRM_XS " for client %s (%ds, %lds)",
+ CRM_XS " for client %s (%ds, %s)",
peer->host, op->action, op->target, op->client_name,
- timeout_one, stonith_watchdog_timeout_ms);
+ timeout_one,
+ pcmk__readable_interval(stonith_watchdog_timeout_ms));
}
op->state = st_exec;
@@ -1887,11 +1968,8 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
op->op_timer_one = 0;
}
- if (!((stonith_watchdog_timeout_ms > 0)
- && (pcmk__str_eq(device, STONITH_WATCHDOG_ID, pcmk__str_none)
- || (pcmk__str_eq(peer->host, op->target, pcmk__str_casei)
- && pcmk__is_fencing_action(op->action)))
- && check_watchdog_fencing_and_wait(op))) {
+ if (!is_watchdog_fencing(op, device)
+ || !check_watchdog_fencing_and_wait(op)) {
/* Some thoughts about self-fencing cases reaching this point:
- Actually check in check_watchdog_fencing_and_wait
@@ -1907,8 +1985,8 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
enabled for a node but the watchdog-fencing-device isn't
explicitly chosen for suicide. Local pe-execution in sbd
may detect the node as unclean and lead to timely suicide.
- Otherwise the selection of stonith-watchdog-timeout at
- least is questionable.
+ Otherwise the selection of PCMK_OPT_STONITH_WATCHDOG_TIMEOUT
+ at least is questionable.
*/
/* coming here we're not waiting for watchdog timeout -
@@ -1916,7 +1994,7 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
op->op_timer_one = g_timeout_add((1000 * timeout_one), remote_op_timeout_one, op);
}
- send_cluster_message(crm_get_peer(0, peer->host), crm_msg_stonith_ng, remote_op, FALSE);
+ pcmk__cluster_send_message(peer_node, crm_msg_stonith_ng, remote_op);
peer->tried = TRUE;
free_xml(remote_op);
return;
@@ -1948,11 +2026,15 @@ request_peer_fencing(remote_fencing_op_t *op, peer_device_info_t *peer)
* but we have all the expected replies, then no devices
* are available to execute the fencing operation. */
- if(stonith_watchdog_timeout_ms > 0 && pcmk__str_eq(device,
- STONITH_WATCHDOG_ID, pcmk__str_null_matches)) {
- if (check_watchdog_fencing_and_wait(op)) {
- return;
- }
+ if (is_watchdog_fencing(op, device)
+ && check_watchdog_fencing_and_wait(op)) {
+ /* Consider a watchdog fencing targeting an offline node executing
+ * once it starts waiting for the target to self-fence. So that when
+ * the query timer pops, remote_op_query_timeout() considers the
+ * fencing already in progress.
+ */
+ op->state = st_exec;
+ return;
}
if (op->state == st_query) {
@@ -2078,24 +2160,25 @@ parse_action_specific(const xmlNode *xml, const char *peer, const char *device,
enum st_remap_phase phase, device_properties_t *props)
{
props->custom_action_timeout[phase] = 0;
- crm_element_value_int(xml, F_STONITH_ACTION_TIMEOUT,
+ crm_element_value_int(xml, PCMK__XA_ST_ACTION_TIMEOUT,
&props->custom_action_timeout[phase]);
if (props->custom_action_timeout[phase]) {
- crm_trace("Peer %s with device %s returned %s action timeout %d",
+ crm_trace("Peer %s with device %s returned %s action timeout %ds",
peer, device, action, props->custom_action_timeout[phase]);
}
props->delay_max[phase] = 0;
- crm_element_value_int(xml, F_STONITH_DELAY_MAX, &props->delay_max[phase]);
+ crm_element_value_int(xml, PCMK__XA_ST_DELAY_MAX, &props->delay_max[phase]);
if (props->delay_max[phase]) {
- crm_trace("Peer %s with device %s returned maximum of random delay %d for %s",
+ crm_trace("Peer %s with device %s returned maximum of random delay %ds for %s",
peer, device, props->delay_max[phase], action);
}
props->delay_base[phase] = 0;
- crm_element_value_int(xml, F_STONITH_DELAY_BASE, &props->delay_base[phase]);
+ crm_element_value_int(xml, PCMK__XA_ST_DELAY_BASE,
+ &props->delay_base[phase]);
if (props->delay_base[phase]) {
- crm_trace("Peer %s with device %s returned base delay %d for %s",
+ crm_trace("Peer %s with device %s returned base delay %ds for %s",
peer, device, props->delay_base[phase], action);
}
@@ -2103,7 +2186,7 @@ parse_action_specific(const xmlNode *xml, const char *peer, const char *device,
if (pcmk__str_eq(action, PCMK_ACTION_ON, pcmk__str_none)) {
int required = 0;
- crm_element_value_int(xml, F_STONITH_DEVICE_REQUIRED, &required);
+ crm_element_value_int(xml, PCMK__XA_ST_REQUIRED, &required);
if (required) {
crm_trace("Peer %s requires device %s to execute for action %s",
peer, device, action);
@@ -2114,7 +2197,7 @@ parse_action_specific(const xmlNode *xml, const char *peer, const char *device,
/* If a reboot is remapped to off+on, it's possible that a node is allowed
* to perform one action but not another.
*/
- if (pcmk__xe_attr_is_true(xml, F_STONITH_ACTION_DISALLOWED)) {
+ if (pcmk__xe_attr_is_true(xml, PCMK__XA_ST_ACTION_DISALLOWED)) {
props->disallowed[phase] = TRUE;
crm_trace("Peer %s is disallowed from executing %s for device %s",
peer, action, device);
@@ -2136,37 +2219,39 @@ add_device_properties(const xmlNode *xml, remote_fencing_op_t *op,
{
xmlNode *child;
int verified = 0;
- device_properties_t *props = calloc(1, sizeof(device_properties_t));
+ device_properties_t *props =
+ pcmk__assert_alloc(1, sizeof(device_properties_t));
int flags = st_device_supports_on; /* Old nodes that don't set the flag assume they support the on action */
/* Add a new entry to this peer's devices list */
- CRM_ASSERT(props != NULL);
- g_hash_table_insert(peer->devices, strdup(device), props);
+ g_hash_table_insert(peer->devices, pcmk__str_copy(device), props);
/* Peers with verified (monitored) access will be preferred */
- crm_element_value_int(xml, F_STONITH_DEVICE_VERIFIED, &verified);
+ crm_element_value_int(xml, PCMK__XA_ST_MONITOR_VERIFIED, &verified);
if (verified) {
crm_trace("Peer %s has confirmed a verified device %s",
peer->host, device);
props->verified = TRUE;
}
- crm_element_value_int(xml, F_STONITH_DEVICE_SUPPORT_FLAGS, &flags);
+ crm_element_value_int(xml, PCMK__XA_ST_DEVICE_SUPPORT_FLAGS, &flags);
props->device_support_flags = flags;
/* Parse action-specific device properties */
parse_action_specific(xml, peer->host, device, op_requested_action(op),
op, st_phase_requested, props);
- for (child = pcmk__xml_first_child(xml); child != NULL;
- child = pcmk__xml_next(child)) {
+ for (child = pcmk__xe_first_child(xml, NULL, NULL, NULL); child != NULL;
+ child = pcmk__xe_next(child)) {
/* Replies for "reboot" operations will include the action-specific
* values for "off" and "on" in child elements, just in case the reboot
* winds up getting remapped.
*/
- if (pcmk__str_eq(ID(child), PCMK_ACTION_OFF, pcmk__str_none)) {
+ if (pcmk__str_eq(pcmk__xe_id(child), PCMK_ACTION_OFF, pcmk__str_none)) {
parse_action_specific(child, peer->host, device, PCMK_ACTION_OFF,
op, st_phase_off, props);
- } else if (pcmk__str_eq(ID(child), PCMK_ACTION_ON, pcmk__str_none)) {
+
+ } else if (pcmk__str_eq(pcmk__xe_id(child), PCMK_ACTION_ON,
+ pcmk__str_none)) {
parse_action_specific(child, peer->host, device, PCMK_ACTION_ON,
op, st_phase_on, props);
}
@@ -2188,19 +2273,17 @@ static peer_device_info_t *
add_result(remote_fencing_op_t *op, const char *host, int ndevices,
const xmlNode *xml)
{
- peer_device_info_t *peer = calloc(1, sizeof(peer_device_info_t));
+ peer_device_info_t *peer = pcmk__assert_alloc(1,
+ sizeof(peer_device_info_t));
xmlNode *child;
- // cppcheck seems not to understand the abort logic in CRM_CHECK
- // cppcheck-suppress memleak
- CRM_CHECK(peer != NULL, return NULL);
- peer->host = strdup(host);
+ peer->host = pcmk__str_copy(host);
peer->devices = pcmk__strkey_table(free, free);
/* Each child element describes one capable device available to the peer */
- for (child = pcmk__xml_first_child(xml); child != NULL;
- child = pcmk__xml_next(child)) {
- const char *device = ID(child);
+ for (child = pcmk__xe_first_child(xml, NULL, NULL, NULL); child != NULL;
+ child = pcmk__xe_next(child)) {
+ const char *device = pcmk__xe_id(child);
if (device) {
add_device_properties(child, op, peer, device);
@@ -2241,16 +2324,16 @@ process_remote_stonith_query(xmlNode *msg)
remote_fencing_op_t *op = NULL;
peer_device_info_t *peer = NULL;
uint32_t replies_expected;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_REMOTE_OP_ID, msg, LOG_ERR);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_REMOTE_OP, msg, LOG_ERR);
CRM_CHECK(dev != NULL, return -EPROTO);
- id = crm_element_value(dev, F_STONITH_REMOTE_OP_ID);
+ id = crm_element_value(dev, PCMK__XA_ST_REMOTE_OP);
CRM_CHECK(id != NULL, return -EPROTO);
- dev = get_xpath_object("//@" F_STONITH_AVAILABLE_DEVICES, msg, LOG_ERR);
+ dev = get_xpath_object("//@" PCMK__XA_ST_AVAILABLE_DEVICES, msg, LOG_ERR);
CRM_CHECK(dev != NULL, return -EPROTO);
- crm_element_value_int(dev, F_STONITH_AVAILABLE_DEVICES, &ndevices);
+ crm_element_value_int(dev, PCMK__XA_ST_AVAILABLE_DEVICES, &ndevices);
op = g_hash_table_lookup(stonith_remote_op_list, id);
if (op == NULL) {
@@ -2266,7 +2349,7 @@ process_remote_stonith_query(xmlNode *msg)
if ((++op->replies >= replies_expected) && (op->state == st_query)) {
have_all_replies = TRUE;
}
- host = crm_element_value(msg, F_ORIG);
+ host = crm_element_value(msg, PCMK__XA_SRC);
host_is_target = pcmk__str_eq(host, op->target, pcmk__str_casei);
crm_info("Query result %d of %d from %s for %s/%s (%d device%s) %s",
@@ -2339,12 +2422,12 @@ fenced_process_fencing_reply(xmlNode *msg)
const char *id = NULL;
const char *device = NULL;
remote_fencing_op_t *op = NULL;
- xmlNode *dev = get_xpath_object("//@" F_STONITH_REMOTE_OP_ID, msg, LOG_ERR);
+ xmlNode *dev = get_xpath_object("//@" PCMK__XA_ST_REMOTE_OP, msg, LOG_ERR);
pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
CRM_CHECK(dev != NULL, return);
- id = crm_element_value(dev, F_STONITH_REMOTE_OP_ID);
+ id = crm_element_value(dev, PCMK__XA_ST_REMOTE_OP);
CRM_CHECK(id != NULL, return);
dev = stonith__find_xe_with_result(msg);
@@ -2352,7 +2435,7 @@ fenced_process_fencing_reply(xmlNode *msg)
stonith__xe_get_result(dev, &result);
- device = crm_element_value(dev, F_STONITH_DEVICE);
+ device = crm_element_value(dev, PCMK__XA_ST_DEVICE_ID);
if (stonith_remote_op_list) {
op = g_hash_table_lookup(stonith_remote_op_list, id);
@@ -2360,7 +2443,7 @@ fenced_process_fencing_reply(xmlNode *msg)
if ((op == NULL) && pcmk__result_ok(&result)) {
/* Record successful fencing operations */
- const char *client_id = crm_element_value(dev, F_STONITH_CLIENTID);
+ const char *client_id = crm_element_value(dev, PCMK__XA_ST_CLIENTID);
op = create_remote_stonith_op(client_id, dev, TRUE);
}
@@ -2383,7 +2466,9 @@ fenced_process_fencing_reply(xmlNode *msg)
return;
}
- if (pcmk__str_eq(crm_element_value(msg, F_SUBTYPE), "broadcast", pcmk__str_casei)) {
+ if (pcmk__str_eq(crm_element_value(msg, PCMK__XA_SUBT),
+ PCMK__VALUE_BROADCAST, pcmk__str_none)) {
+
if (pcmk__result_ok(&op->result)) {
op->state = st_done;
} else {
@@ -2412,7 +2497,7 @@ fenced_process_fencing_reply(xmlNode *msg)
return;
}
- device = crm_element_value(msg, F_STONITH_DEVICE);
+ device = crm_element_value(msg, PCMK__XA_ST_DEVICE_ID);
if ((op->phase == 2) && !pcmk__result_ok(&op->result)) {
/* A remapped "on" failed, but the node was already turned off
diff --git a/daemons/fenced/fenced_scheduler.c b/daemons/fenced/fenced_scheduler.c
index 27d990f..69e16fa 100644
--- a/daemons/fenced/fenced_scheduler.c
+++ b/daemons/fenced/fenced_scheduler.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.
*
@@ -124,14 +124,14 @@ register_if_fencing_device(gpointer data, gpointer user_data)
if (rsc->children != NULL) {
for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
register_if_fencing_device(iter->data, NULL);
- if (pe_rsc_is_clone(rsc)) {
+ if (pcmk__is_clone(rsc)) {
return; // Only one instance needs to be checked for clones
}
}
return;
}
- rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
+ rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS);
if (!pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
return; // Not a fencing device
}
@@ -163,8 +163,7 @@ register_if_fencing_device(gpointer data, gpointer user_data)
}
// If device is in a group, check whether local node is allowed for group
- if ((rsc->parent != NULL)
- && (rsc->parent->variant == pcmk_rsc_variant_group)) {
+ if (pcmk__is_group(rsc->parent)) {
pcmk_node_t *group_node = local_node_allowed_for(rsc->parent);
if ((group_node != NULL) && (group_node->weight < 0)) {
@@ -177,8 +176,12 @@ register_if_fencing_device(gpointer data, gpointer user_data)
crm_debug("Reloading configuration of fencing device %s", rsc->id);
- agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE);
+ agent = crm_element_value(rsc->xml, PCMK_XA_TYPE);
+ /* @COMPAT Support for node attribute expressions in rules for resource
+ * meta-attributes is deprecated. When we can break behavioral backward
+ * compatibility, replace node with NULL here.
+ */
get_meta_attributes(rsc->meta, rsc, node, scheduler);
rsc_provides = g_hash_table_lookup(rsc->meta, PCMK_STONITH_PROVIDES);
diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c
index 7c69fb8..5ba97b5 100644
--- a/daemons/fenced/pacemaker-fenced.c
+++ b/daemons/fenced/pacemaker-fenced.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.
*
@@ -22,7 +22,6 @@
#include <inttypes.h> // PRIu32, PRIx32
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc.h>
#include <crm/common/ipc_internal.h>
@@ -42,7 +41,7 @@
#define SUMMARY "daemon for executing fencing devices in a Pacemaker cluster"
char *stonith_our_uname = NULL;
-long stonith_watchdog_timeout_ms = 0;
+long long stonith_watchdog_timeout_ms = 0;
GList *stonith_watchdog_targets = NULL;
static GMainLoop *mainloop = NULL;
@@ -75,11 +74,11 @@ st_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
if (stonith_shutdown_flag) {
crm_info("Ignoring new client [%d] during shutdown",
pcmk__client_pid(c));
- return -EPERM;
+ return -ECONNREFUSED;
}
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -102,34 +101,31 @@ st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
request = pcmk__client_data2xml(c, data, &id, &flags);
if (request == NULL) {
- pcmk__ipc_send_ack(c, id, flags, "nack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(c, id, flags, PCMK__XE_NACK, NULL, CRM_EX_PROTOCOL);
return 0;
}
- op = crm_element_value(request, F_CRM_TASK);
+ op = crm_element_value(request, PCMK__XA_CRM_TASK);
if(pcmk__str_eq(op, CRM_OP_RM_NODE_CACHE, pcmk__str_casei)) {
- crm_xml_add(request, F_TYPE, T_STONITH_NG);
- crm_xml_add(request, F_STONITH_OPERATION, op);
- crm_xml_add(request, F_STONITH_CLIENTID, c->id);
- crm_xml_add(request, F_STONITH_CLIENTNAME, pcmk__client_name(c));
- crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
+ crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
+ crm_xml_add(request, PCMK__XA_ST_OP, op);
+ crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
+ crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
+ crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, stonith_our_uname);
- send_cluster_message(NULL, crm_msg_stonith_ng, request, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, request);
free_xml(request);
return 0;
}
if (c->name == NULL) {
- const char *value = crm_element_value(request, F_STONITH_CLIENTNAME);
+ const char *value = crm_element_value(request, PCMK__XA_ST_CLIENTNAME);
- if (value == NULL) {
- value = "unknown";
- }
- c->name = crm_strdup_printf("%s.%u", value, c->pid);
+ c->name = crm_strdup_printf("%s.%u", pcmk__s(value, "unknown"), c->pid);
}
- crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options);
+ crm_element_value_int(request, PCMK__XA_ST_CALLOPT, &call_options);
crm_trace("Flags %#08" PRIx32 "/%#08x for command %" PRIu32
" from client %s", flags, call_options, id, pcmk__client_name(c));
@@ -139,9 +135,9 @@ st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
c->request_id = id; /* Reply only to the last one */
}
- crm_xml_add(request, F_STONITH_CLIENTID, c->id);
- crm_xml_add(request, F_STONITH_CLIENTNAME, pcmk__client_name(c));
- crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
+ crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
+ crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
+ crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, stonith_our_uname);
crm_log_xml_trace(request, "ipc-received");
stonith_command(c, id, flags, request, NULL);
@@ -177,10 +173,10 @@ st_ipc_destroy(qb_ipcs_connection_t * c)
static void
stonith_peer_callback(xmlNode * msg, void *private_data)
{
- const char *remote_peer = crm_element_value(msg, F_ORIG);
- const char *op = crm_element_value(msg, F_STONITH_OPERATION);
+ const char *remote_peer = crm_element_value(msg, PCMK__XA_SRC);
+ const char *op = crm_element_value(msg, PCMK__XA_ST_OP);
- if (pcmk__str_eq(op, "poke", pcmk__str_none)) {
+ if (pcmk__str_eq(op, STONITH_OP_POKE, pcmk__str_none)) {
return;
}
@@ -197,20 +193,19 @@ stonith_peer_ais_callback(cpg_handle_t handle,
uint32_t kind = 0;
xmlNode *xml = NULL;
const char *from = NULL;
- char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
+ char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
if(data == NULL) {
return;
}
if (kind == crm_class_cluster) {
- xml = string2xml(data);
+ xml = pcmk__xml_parse(data);
if (xml == NULL) {
crm_err("Invalid XML: '%.120s'", data);
free(data);
return;
}
- crm_xml_add(xml, F_ORIG, from);
- /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
+ crm_xml_add(xml, PCMK__XA_SRC, from);
stonith_peer_callback(xml, NULL);
}
@@ -257,7 +252,7 @@ do_local_reply(const xmlNode *notify_src, pcmk__client_t *client,
uint64_t
get_stonith_flag(const char *name)
{
- if (pcmk__str_eq(name, T_STONITH_NOTIFY_FENCE, pcmk__str_casei)) {
+ if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_FENCE, pcmk__str_none)) {
return st_callback_notify_fence;
} else if (pcmk__str_eq(name, STONITH_OP_DEVICE_ADD, pcmk__str_casei)) {
@@ -266,10 +261,12 @@ get_stonith_flag(const char *name)
} else if (pcmk__str_eq(name, STONITH_OP_DEVICE_DEL, pcmk__str_casei)) {
return st_callback_device_del;
- } else if (pcmk__str_eq(name, T_STONITH_NOTIFY_HISTORY, pcmk__str_casei)) {
+ } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY,
+ pcmk__str_none)) {
return st_callback_notify_history;
- } else if (pcmk__str_eq(name, T_STONITH_NOTIFY_HISTORY_SYNCED, pcmk__str_casei)) {
+ } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED,
+ pcmk__str_none)) {
return st_callback_notify_history_synced;
}
@@ -287,7 +284,7 @@ stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
CRM_CHECK(client != NULL, return);
CRM_CHECK(update_msg != NULL, return);
- type = crm_element_value(update_msg, F_SUBTYPE);
+ type = crm_element_value(update_msg, PCMK__XA_SUBT);
CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
if (client->ipcs == NULL) {
@@ -325,10 +322,10 @@ do_stonith_async_timeout_update(const char *client_id, const char *call_id, int
return;
}
- notify_data = create_xml_node(NULL, T_STONITH_TIMEOUT_VALUE);
- crm_xml_add(notify_data, F_TYPE, T_STONITH_TIMEOUT_VALUE);
- crm_xml_add(notify_data, F_STONITH_CALLID, call_id);
- crm_xml_add_int(notify_data, F_STONITH_TIMEOUT, timeout);
+ notify_data = pcmk__xe_create(NULL, PCMK__XE_ST_ASYNC_TIMEOUT_VALUE);
+ crm_xml_add(notify_data, PCMK__XA_T, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE);
+ crm_xml_add(notify_data, PCMK__XA_ST_CALLID, call_id);
+ crm_xml_add_int(notify_data, PCMK__XA_ST_TIMEOUT, timeout);
crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
@@ -352,17 +349,19 @@ fenced_send_notification(const char *type, const pcmk__action_result_t *result,
xmlNode *data)
{
/* TODO: Standardize the contents of data */
- xmlNode *update_msg = create_xml_node(NULL, "notify");
+ xmlNode *update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
CRM_LOG_ASSERT(type != NULL);
- crm_xml_add(update_msg, F_TYPE, T_STONITH_NOTIFY);
- crm_xml_add(update_msg, F_SUBTYPE, type);
- crm_xml_add(update_msg, F_STONITH_OPERATION, type);
+ crm_xml_add(update_msg, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
+ crm_xml_add(update_msg, PCMK__XA_SUBT, type);
+ crm_xml_add(update_msg, PCMK__XA_ST_OP, type);
stonith__xe_set_result(update_msg, result);
if (data != NULL) {
- add_message_xml(update_msg, F_STONITH_CALLDATA, data);
+ xmlNode *wrapper = pcmk__xe_create(update_msg, PCMK__XE_ST_CALLDATA);
+
+ pcmk__xml_copy(wrapper, data);
}
crm_trace("Notifying clients");
@@ -375,60 +374,25 @@ fenced_send_notification(const char *type, const pcmk__action_result_t *result,
* \internal
* \brief Send notifications for a configuration change to subscribed clients
*
- * \param[in] op Notification type (STONITH_OP_DEVICE_ADD,
- * STONITH_OP_DEVICE_DEL, STONITH_OP_LEVEL_ADD, or
- * STONITH_OP_LEVEL_DEL)
- * \param[in] result Operation result
- * \param[in] desc Description of what changed
- * \param[in] active Current number of devices or topologies in use
- */
-static void
-send_config_notification(const char *op, const pcmk__action_result_t *result,
- const char *desc, int active)
-{
- xmlNode *notify_data = create_xml_node(NULL, op);
-
- CRM_CHECK(notify_data != NULL, return);
-
- crm_xml_add(notify_data, F_STONITH_DEVICE, desc);
- crm_xml_add_int(notify_data, F_STONITH_ACTIVE, active);
-
- fenced_send_notification(op, result, notify_data);
- free_xml(notify_data);
-}
-
-/*!
- * \internal
- * \brief Send notifications for a device change to subscribed clients
- *
- * \param[in] op Notification type (STONITH_OP_DEVICE_ADD or
- * STONITH_OP_DEVICE_DEL)
+ * \param[in] op Notification type (\c STONITH_OP_DEVICE_ADD,
+ * \c STONITH_OP_DEVICE_DEL, \c STONITH_OP_LEVEL_ADD, or
+ * \c STONITH_OP_LEVEL_DEL)
* \param[in] result Operation result
- * \param[in] desc ID of device that changed
+ * \param[in] desc Description of what changed (either device ID or string
+ * representation of level
+ * (<tt><target>[<level_index>]</tt>))
*/
void
-fenced_send_device_notification(const char *op,
+fenced_send_config_notification(const char *op,
const pcmk__action_result_t *result,
const char *desc)
{
- send_config_notification(op, result, desc, g_hash_table_size(device_list));
-}
+ xmlNode *notify_data = pcmk__xe_create(NULL, op);
-/*!
- * \internal
- * \brief Send notifications for a topology level change to subscribed clients
- *
- * \param[in] op Notification type (STONITH_OP_LEVEL_ADD or
- * STONITH_OP_LEVEL_DEL)
- * \param[in] result Operation result
- * \param[in] desc String representation of level (<target>[<level_index>])
- */
-void
-fenced_send_level_notification(const char *op,
- const pcmk__action_result_t *result,
- const char *desc)
-{
- send_config_notification(op, result, desc, g_hash_table_size(topology));
+ crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ID, desc);
+
+ fenced_send_notification(op, result, notify_data);
+ free_xml(notify_data);
}
/*!
@@ -466,7 +430,7 @@ stonith_cleanup(void)
qb_ipcs_destroy(ipcs);
}
- crm_peer_destroy();
+ pcmk__cluster_destroy_node_caches();
pcmk__client_cleanup();
free_stonith_remote_op_list();
free_topology_list();
@@ -512,221 +476,34 @@ st_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void
* This is a hack until we can send to a nodeid and/or we fix node name lookups
* These messages are ignored in stonith_peer_callback()
*/
- xmlNode *query = create_xml_node(NULL, "stonith_command");
+ xmlNode *query = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
- crm_xml_add(query, F_XML_TAGNAME, "stonith_command");
- crm_xml_add(query, F_TYPE, T_STONITH_NG);
- crm_xml_add(query, F_STONITH_OPERATION, "poke");
+ crm_xml_add(query, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
+ crm_xml_add(query, PCMK__XA_ST_OP, STONITH_OP_POKE);
crm_debug("Broadcasting our uname because of node %u", node->id);
- send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
+ pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, query);
free_xml(query);
}
}
-static pcmk__cluster_option_t fencer_options[] = {
- /* name, old name, type, allowed values,
- * default value, validator,
- * short description,
- * long description
- */
- {
- PCMK_STONITH_HOST_ARGUMENT, NULL, "string", NULL, "port", NULL,
- N_("Advanced use only: 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, "string", NULL, "", NULL,
- N_("A mapping of host names to ports numbers for devices that do not support host names."),
- N_("Eg. 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, "string", NULL, "", NULL,
- N_("Eg. node1,node2,node3"),
- N_("A list of machines controlled by "
- "this device (Optional unless pcmk_host_list=static-list)")
- },
- {
- PCMK_STONITH_HOST_CHECK,NULL, "string", NULL, "dynamic-list", NULL,
- N_("How to determine which machines are controlled by the device."),
- N_("Allowed values: dynamic-list "
- "(query the device via the 'list' command), static-list "
- "(check the pcmk_host_list attribute), status "
- "(query the device via the 'status' command), "
- "none (assume every device can fence every "
- "machine)")
- },
- {
- PCMK_STONITH_DELAY_MAX,NULL, "time", NULL, "0s", NULL,
- N_("Enable a base delay for fencing actions and specify base delay value."),
- 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, "string", NULL, "0s", NULL,
- 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 per target.")
- },
- {
- PCMK_STONITH_ACTION_LIMIT,NULL, "integer", NULL, "1", NULL,
- 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. -1 is unlimited.")
- },
- {
- "pcmk_reboot_action", NULL, "string", NULL,
- PCMK_ACTION_REBOOT, NULL,
- N_("Advanced use only: An alternate command to run instead of 'reboot'"),
- N_("Some devices do not support the standard commands or may provide additional ones.\n"
- "Use this to specify an alternate, device-specific, command that implements the \'reboot\' action.")
- },
- {
- "pcmk_reboot_timeout",NULL, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'reboot\' actions before giving up.")
- },
- {
- "pcmk_off_action", NULL, "string", NULL,
- PCMK_ACTION_OFF, NULL,
- N_("Advanced use only: 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, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'off\' actions before giving up.")
- },
- {
- "pcmk_on_action", NULL, "string", NULL,
- PCMK_ACTION_ON, NULL,
- N_("Advanced use only: 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, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'on\' actions before giving up.")
- },
- {
- "pcmk_list_action",NULL, "string", NULL,
- PCMK_ACTION_LIST, NULL,
- N_("Advanced use only: 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, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'list\' actions before giving up.")
- },
- {
- "pcmk_monitor_action", NULL, "string", NULL,
- PCMK_ACTION_MONITOR, NULL,
- N_("Advanced use only: 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, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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.\n"
- "Use this to specify an alternate, device-specific, timeout for \'monitor\' actions.")
- },
- {
- "pcmk_monitor_retries",NULL, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'monitor\' actions before giving up.")
- },
- {
- "pcmk_status_action", NULL, "string", NULL,
- PCMK_ACTION_STATUS, NULL,
- N_("Advanced use only: 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, "time", NULL, "60s", NULL,
- N_("Advanced use only: 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, "integer", NULL, "2", NULL,
- N_("Advanced use only: The maximum number of times to retry 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 so Pacemaker will automatically retry the operation, if there is time remaining."
- " Use this option to alter the number of times Pacemaker retries \'status\' actions before giving up.")
- },
-};
-
-void
+/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_fence_attrs() or
+ * crm_resource --list-options=fencing instead of querying daemon metadata.
+ */
+static int
fencer_metadata(void)
{
+ const char *name = "pacemaker-fenced";
const char *desc_short = N_("Instance attributes available for all "
- "\"stonith\"-class resources");
- const char *desc_long = N_("Instance attributes available for all \"stonith\"-"
- "class resources and used by Pacemaker's fence "
- "daemon, formerly known as stonithd");
-
- gchar *s = pcmk__format_option_metadata("pacemaker-fenced", desc_short,
- desc_long, fencer_options,
- PCMK__NELEM(fencer_options));
- printf("%s", s);
- g_free(s);
+ "\"stonith\"-class resources");
+ const char *desc_long = N_("Instance attributes available for all "
+ "\"stonith\"-class resources and used by "
+ "Pacemaker's fence daemon, formerly known as "
+ "stonithd");
+
+ return pcmk__daemon_metadata(out, name, desc_short, desc_long,
+ pcmk__opt_fencing);
}
static GOptionEntry entries[] = {
@@ -747,8 +524,7 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
{
GOptionContext *context = NULL;
- context = pcmk__build_arg_context(args, "text (default), xml", group,
- "[metadata]");
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
pcmk__add_main_args(context, entries);
return context;
}
@@ -757,7 +533,7 @@ int
main(int argc, char **argv)
{
int rc = pcmk_rc_ok;
- crm_cluster_t *cluster = NULL;
+ pcmk_cluster_t *cluster = NULL;
crm_ipc_t *old_instance = NULL;
GError *error = NULL;
@@ -791,7 +567,13 @@ main(int argc, char **argv)
if ((g_strv_length(processed_args) >= 2)
&& pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
- fencer_metadata();
+
+ rc = fencer_metadata();
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Unable to display metadata: %s", pcmk_rc_str(rc));
+ }
goto done;
}
@@ -826,7 +608,7 @@ main(int argc, char **argv)
mainloop_add_signal(SIGTERM, stonith_shutdown);
- crm_peer_init();
+ pcmk__cluster_init_node_caches();
rc = fenced_scheduler_init();
if (rc != pcmk_rc_ok) {
@@ -840,16 +622,16 @@ main(int argc, char **argv)
if (!stand_alone) {
#if SUPPORT_COROSYNC
- if (is_corosync_cluster()) {
- cluster->destroy = stonith_peer_cs_destroy;
- cluster->cpg.cpg_deliver_fn = stonith_peer_ais_callback;
- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
+ pcmk_cluster_set_destroy_fn(cluster, stonith_peer_cs_destroy);
+ pcmk_cpg_set_deliver_fn(cluster, stonith_peer_ais_callback);
+ pcmk_cpg_set_confchg_fn(cluster, pcmk__cpg_confchg_cb);
}
#endif // SUPPORT_COROSYNC
- crm_set_status_callback(&st_peer_update_callback);
+ pcmk__cluster_set_status_callback(&st_peer_update_callback);
- if (crm_cluster_connect(cluster) == FALSE) {
+ if (pcmk_cluster_connect(cluster) != pcmk_rc_ok) {
exit_code = CRM_EX_FATAL;
crm_crit("Cannot sign in to the cluster... terminating");
goto done;
diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h
index 220978a..2d8047c 100644
--- a/daemons/fenced/pacemaker-fenced.h
+++ b/daemons/fenced/pacemaker-fenced.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2023 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
@@ -111,8 +111,8 @@ typedef struct remote_fencing_op_s {
/*!
* Fencing delay (in seconds) requested by API client (used by controller to
- * implement priority-fencing-delay). A value of -1 means disable all
- * configured delays.
+ * implement \c PCMK_OPT_PRIORITY_FENCING_DELAY). A value of -1 means
+ * disable all configured delays.
*/
int client_delay;
@@ -253,12 +253,9 @@ void
void fenced_send_notification(const char *type,
const pcmk__action_result_t *result,
xmlNode *data);
-void fenced_send_device_notification(const char *op,
+void fenced_send_config_notification(const char *op,
const pcmk__action_result_t *result,
const char *desc);
-void fenced_send_level_notification(const char *op,
- const pcmk__action_result_t *result,
- const char *desc);
remote_fencing_op_t *initiate_remote_stonith_op(const pcmk__client_t *client,
xmlNode *request,
@@ -281,7 +278,6 @@ void set_fencing_completed(remote_fencing_op_t * op);
int fenced_handle_manual_confirmation(const pcmk__client_t *client,
xmlNode *msg);
-void fencer_metadata(void);
const char *fenced_device_reboot_action(const char *device_id);
bool fenced_device_supports_on(const char *device_id);
@@ -327,7 +323,7 @@ extern char *stonith_our_uname;
extern gboolean stand_alone;
extern GHashTable *device_list;
extern GHashTable *topology;
-extern long stonith_watchdog_timeout_ms;
+extern long long stonith_watchdog_timeout_ms;
extern GList *stonith_watchdog_targets;
extern GHashTable *stonith_remote_op_list;
extern crm_exit_t exit_code;
diff --git a/daemons/pacemakerd/Makefile.am b/daemons/pacemakerd/Makefile.am
index 78e7c37..7571a6c 100644
--- a/daemons/pacemakerd/Makefile.am
+++ b/daemons/pacemakerd/Makefile.am
@@ -20,7 +20,8 @@ EXTRA_DIST = pacemakerd.8.inc
## SOURCES
-noinst_HEADERS = pacemakerd.h
+noinst_HEADERS = pacemakerd.h \
+ pcmkd_corosync.h
pacemakerd_CFLAGS = $(CFLAGS_HARDENED_EXE)
pacemakerd_LDFLAGS = $(LDFLAGS_HARDENED_EXE)
diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
index 365b743..5d5a1db 100644
--- a/daemons/pacemakerd/pacemakerd.c
+++ b/daemons/pacemakerd/pacemakerd.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.
*
@@ -10,6 +10,10 @@
#include <crm_internal.h>
#include "pacemakerd.h"
+#if SUPPORT_COROSYNC
+#include "pcmkd_corosync.h"
+#endif
+
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
@@ -21,8 +25,8 @@
#include <sys/resource.h>
#include <crm/crm.h> /* indirectly: CRM_EX_* */
-#include <crm/msg_xml.h>
#include <crm/common/mainloop.h>
+#include <crm/common/xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc_pacemakerd.h>
#include <crm/common/output_internal.h>
@@ -60,19 +64,21 @@ static int
pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
- pcmk__output_xml_create_parent(out, "pacemakerd",
- "version", PACEMAKER_VERSION,
- "build", BUILD_VERSION,
- "feature_set", CRM_FEATURE_SET,
+ pcmk__output_xml_create_parent(out, PCMK_XE_PACEMAKERD,
+ PCMK_XA_VERSION, PACEMAKER_VERSION,
+ PCMK_XA_BUILD, BUILD_VERSION,
+ PCMK_XA_FEATURE_SET, CRM_FEATURE_SET,
NULL);
- out->begin_list(out, NULL, NULL, "features");
+ out->begin_list(out, NULL, NULL, PCMK_XE_FEATURES);
for (char **s = feature_list; *s != NULL; s++) {
- pcmk__output_create_xml_text_node(out, "feature", *s);
+ pcmk__output_create_xml_text_node(out, PCMK_XE_FEATURE, *s);
}
out->end_list(out);
+ pcmk__output_xml_pop_parent(out);
+
g_strfreev(feature_list);
return pcmk_rc_ok;
}
@@ -92,7 +98,7 @@ pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **er
static gboolean
standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
options.standby = TRUE;
- pcmk__set_env_option(PCMK__ENV_NODE_START_STATE, "standby", false);
+ pcmk__set_env_option(PCMK__ENV_NODE_START_STATE, PCMK_VALUE_STANDBY, false);
return TRUE;
}
@@ -297,8 +303,6 @@ main(int argc, char **argv)
goto done;
}
- pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
-
pcmk__register_messages(out, fmt_functions);
if (options.features) {
@@ -313,7 +317,7 @@ main(int argc, char **argv)
}
// @COMPAT Drop at 3.0.0; likely last used in 1.1.24
- pcmk__set_env_option(PCMK__ENV_MCP, "true", true);
+ pcmk__set_env_option(PCMK__ENV_MCP, PCMK_VALUE_TRUE, true);
if (options.shutdown) {
pcmk__cli_init_logging("pacemakerd", args->verbosity);
@@ -402,7 +406,7 @@ main(int argc, char **argv)
{
const char *facility = pcmk__env_option(PCMK__ENV_LOGFACILITY);
- if (!pcmk__str_eq(facility, PCMK__VALUE_NONE,
+ if (!pcmk__str_eq(facility, PCMK_VALUE_NONE,
pcmk__str_casei|pcmk__str_null_matches)) {
pcmk__set_env_option("LOGFACILITY", facility, true);
}
@@ -444,7 +448,7 @@ main(int argc, char **argv)
if ((running_with_sbd) && pcmk__get_sbd_sync_resource_startup()) {
crm_notice("Waiting for startup-trigger from SBD.");
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_WAITPING;
+ pacemakerd_state = PCMK__VALUE_WAIT_FOR_PING;
startup_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, init_children_processes, NULL);
} else {
if (running_with_sbd) {
@@ -452,7 +456,7 @@ main(int argc, char **argv)
"by your SBD version) improve reliability of "
"interworking between SBD & pacemaker.");
}
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS;
+ pacemakerd_state = PCMK__VALUE_STARTING_DAEMONS;
init_children_processes(NULL);
}
diff --git a/daemons/pacemakerd/pacemakerd.h b/daemons/pacemakerd/pacemakerd.h
index ee6facf..51e32b1 100644
--- a/daemons/pacemakerd/pacemakerd.h
+++ b/daemons/pacemakerd/pacemakerd.h
@@ -9,27 +9,19 @@
#include <crm_internal.h>
-#include <stdint.h>
-
#define MAX_RESPAWN 100
extern GMainLoop *mainloop;
extern struct qb_ipcs_service_handlers pacemakerd_ipc_callbacks;
extern const char *pacemakerd_state;
extern gboolean running_with_sbd;
-extern unsigned int shutdown_complete_state_reported_to;
extern gboolean shutdown_complete_state_reported_client_closed;
+extern unsigned int shutdown_complete_state_reported_to;
extern crm_trigger_t *shutdown_trigger;
extern crm_trigger_t *startup_trigger;
extern time_t subdaemon_check_progress;
-gboolean pacemakerd_read_config(void);
-
-gboolean cluster_connect_cfg(void);
-void cluster_disconnect_cfg(void);
int find_and_track_existing_processes(void);
gboolean init_children_processes(void *user_data);
-void restart_cluster_subdaemons(void);
void pcmk_shutdown(int nsig);
-void pcmkd_shutdown_corosync(void);
-bool pcmkd_corosync_connected(void);
+void restart_cluster_subdaemons(void);
diff --git a/daemons/pacemakerd/pcmkd_corosync.c b/daemons/pacemakerd/pcmkd_corosync.c
index 8a1a867..43f6231 100644
--- a/daemons/pacemakerd/pcmkd_corosync.c
+++ b/daemons/pacemakerd/pcmkd_corosync.c
@@ -9,6 +9,7 @@
#include <crm_internal.h>
#include "pacemakerd.h"
+#include "pcmkd_corosync.h"
#include <sys/utsname.h>
#include <sys/stat.h> /* for calls to stat() */
@@ -271,7 +272,8 @@ pacemakerd_read_config(void)
gid_t found_gid = 0;
pid_t found_pid = 0;
int rv;
- enum cluster_type_e stack;
+ enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown;
+ const char *cluster_layer_s = NULL;
// There can be only one possibility
do {
@@ -318,19 +320,21 @@ pacemakerd_read_config(void)
return FALSE;
}
- stack = get_cluster_type();
- if (stack != pcmk_cluster_corosync) {
+ cluster_layer = pcmk_get_cluster_layer();
+ cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
+
+ if (cluster_layer != pcmk_cluster_layer_corosync) {
crm_crit("Expected Corosync cluster layer but detected %s "
- CRM_XS " stack=%d", name_for_cluster_type(stack), stack);
+ CRM_XS " cluster_layer=%d",
+ cluster_layer_s, cluster_layer);
return FALSE;
}
- crm_info("Reading configuration for %s stack",
- name_for_cluster_type(stack));
- pcmk__set_env_option(PCMK__ENV_CLUSTER_TYPE, "corosync", true);
+ crm_info("Reading configuration for %s cluster layer", cluster_layer_s);
+ pcmk__set_env_option(PCMK__ENV_CLUSTER_TYPE, PCMK_VALUE_COROSYNC, true);
// @COMPAT Drop at 3.0.0; added unused in 1.1.9
- pcmk__set_env_option(PCMK__ENV_QUORUM_TYPE, "corosync", true);
+ pcmk__set_env_option(PCMK__ENV_QUORUM_TYPE, PCMK_VALUE_COROSYNC, true);
// If debug logging is not configured, check whether corosync has it
if (pcmk__env_option(PCMK__ENV_DEBUG) == NULL) {
diff --git a/daemons/pacemakerd/pcmkd_corosync.h b/daemons/pacemakerd/pcmkd_corosync.h
new file mode 100644
index 0000000..8c4a1e1
--- /dev/null
+++ b/daemons/pacemakerd/pcmkd_corosync.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010-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>
+
+gboolean cluster_connect_cfg(void);
+void cluster_disconnect_cfg(void);
+gboolean pacemakerd_read_config(void);
+bool pcmkd_corosync_connected(void);
+void pcmkd_shutdown_corosync(void);
diff --git a/daemons/pacemakerd/pcmkd_messages.c b/daemons/pacemakerd/pcmkd_messages.c
index 4e6f822..9837d5a 100644
--- a/daemons/pacemakerd/pcmkd_messages.c
+++ b/daemons/pacemakerd/pcmkd_messages.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2022 the Pacemaker project contributors
+ * Copyright 2010-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -11,7 +11,7 @@
#include "pacemakerd.h"
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <errno.h>
#include <stdbool.h>
@@ -30,7 +30,7 @@ handle_node_cache_request(pcmk__request_t *request)
pcmk__client_name(request->ipc_client));
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_OK);
+ PCMK__XE_ACK, NULL, CRM_EX_OK);
return NULL;
}
@@ -42,23 +42,24 @@ handle_ping_request(pcmk__request_t *request)
const char *value = NULL;
xmlNode *ping = NULL;
xmlNode *reply = NULL;
- const char *from = crm_element_value(msg, F_CRM_SYS_FROM);
+ const char *from = crm_element_value(msg, PCMK__XA_CRM_SYS_FROM);
/* Pinged for status */
- crm_trace("Pinged from " F_CRM_SYS_FROM "='%s' " F_CRM_ORIGIN "='%s'",
+ crm_trace("Pinged from " PCMK__XA_CRM_SYS_FROM "='%s' "
+ PCMK_XA_ORIGIN "='%s'",
pcmk__s(from, ""),
- pcmk__s(crm_element_value(msg, F_CRM_ORIGIN), ""));
+ pcmk__s(crm_element_value(msg, PCMK_XA_ORIGIN), ""));
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INDETERMINATE);
+ PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
- ping = create_xml_node(NULL, XML_CRM_TAG_PING);
- value = crm_element_value(msg, F_CRM_SYS_TO);
- crm_xml_add(ping, XML_PING_ATTR_SYSFROM, value);
- crm_xml_add(ping, XML_PING_ATTR_PACEMAKERDSTATE, pacemakerd_state);
- crm_xml_add_ll(ping, XML_ATTR_TSTAMP,
+ ping = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
+ value = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
+ crm_xml_add(ping, PCMK__XA_CRM_SUBSYSTEM, value);
+ crm_xml_add(ping, PCMK__XA_PACEMAKERD_STATE, pacemakerd_state);
+ crm_xml_add_ll(ping, PCMK_XA_CRM_TIMESTAMP,
(long long) subdaemon_check_progress);
- crm_xml_add(ping, XML_PING_ATTR_STATUS, "ok");
+ crm_xml_add(ping, PCMK_XA_RESULT, "ok");
reply = create_reply(msg, ping);
free_xml(ping);
@@ -73,16 +74,18 @@ handle_ping_request(pcmk__request_t *request)
/* just proceed state on sbd pinging us */
if (from && strstr(from, "sbd")) {
- if (pcmk__str_eq(pacemakerd_state, XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE, pcmk__str_none)) {
+ if (pcmk__str_eq(pacemakerd_state, PCMK__VALUE_SHUTDOWN_COMPLETE,
+ pcmk__str_none)) {
if (pcmk__get_sbd_sync_resource_startup()) {
crm_notice("Shutdown-complete-state passed to SBD.");
}
shutdown_complete_state_reported_to = request->ipc_client->pid;
- } else if (pcmk__str_eq(pacemakerd_state, XML_PING_ATTR_PACEMAKERDSTATE_WAITPING, pcmk__str_none)) {
+ } else if (pcmk__str_eq(pacemakerd_state, PCMK__VALUE_WAIT_FOR_PING,
+ pcmk__str_none)) {
crm_notice("Received startup-trigger from SBD.");
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS;
+ pacemakerd_state = PCMK__VALUE_STARTING_DAEMONS;
mainloop_set_trigger(startup_trigger);
}
}
@@ -105,19 +108,19 @@ handle_shutdown_request(pcmk__request_t *request)
bool allowed = pcmk_is_set(request->ipc_client->flags, pcmk__client_privileged);
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INDETERMINATE);
+ PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
- shutdown = create_xml_node(NULL, XML_CIB_ATTR_SHUTDOWN);
+ shutdown = pcmk__xe_create(NULL, PCMK__XE_SHUTDOWN);
if (allowed) {
crm_notice("Shutting down in response to IPC request %s from %s",
- crm_element_value(msg, F_CRM_REFERENCE),
- crm_element_value(msg, F_CRM_ORIGIN));
- crm_xml_add_int(shutdown, XML_LRM_ATTR_OPSTATUS, CRM_EX_OK);
+ crm_element_value(msg, PCMK_XA_REFERENCE),
+ crm_element_value(msg, PCMK_XA_ORIGIN));
+ crm_xml_add_int(shutdown, PCMK__XA_OP_STATUS, CRM_EX_OK);
} else {
crm_warn("Ignoring shutdown request from unprivileged client %s",
pcmk__client_name(request->ipc_client));
- crm_xml_add_int(shutdown, XML_LRM_ATTR_OPSTATUS, CRM_EX_INSUFFICIENT_PRIV);
+ crm_xml_add_int(shutdown, PCMK__XA_OP_STATUS, CRM_EX_INSUFFICIENT_PRIV);
}
reply = create_reply(msg, shutdown);
@@ -142,7 +145,7 @@ static xmlNode *
handle_unknown_request(pcmk__request_t *request)
{
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INVALID_PARAM);
+ PCMK__XE_ACK, NULL, CRM_EX_INVALID_PARAM);
pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
"Unknown IPC request type '%s' (bug?)",
@@ -168,7 +171,7 @@ pcmk_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
crm_trace("Connection %p", c);
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -217,7 +220,7 @@ pcmk_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
msg = pcmk__client_data2xml(c, data, &id, &flags);
if (msg == NULL) {
- pcmk__ipc_send_ack(c, id, flags, "ack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL, CRM_EX_PROTOCOL);
return 0;
} else {
@@ -235,7 +238,7 @@ pcmk_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
.result = PCMK__UNKNOWN_RESULT,
};
- request.op = crm_element_value_copy(request.xml, F_CRM_TASK);
+ request.op = crm_element_value_copy(request.xml, PCMK__XA_CRM_TASK);
CRM_CHECK(request.op != NULL, return 0);
reply = pcmk__process_request(&request, pcmkd_handlers);
diff --git a/daemons/pacemakerd/pcmkd_subdaemons.c b/daemons/pacemakerd/pcmkd_subdaemons.c
index 21e432e..5bd3512 100644
--- a/daemons/pacemakerd/pcmkd_subdaemons.c
+++ b/daemons/pacemakerd/pcmkd_subdaemons.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.
*
@@ -10,6 +10,10 @@
#include <crm_internal.h>
#include "pacemakerd.h"
+#if SUPPORT_COROSYNC
+#include "pcmkd_corosync.h"
+#endif
+
#include <errno.h>
#include <grp.h>
#include <signal.h>
@@ -21,22 +25,25 @@
#include <unistd.h>
#include <crm/cluster.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
+
+enum child_daemon_flags {
+ child_none = 0,
+ child_respawn = 1 << 0,
+ child_needs_cluster = 1 << 1,
+ child_needs_retry = 1 << 2,
+ child_active_before_startup = 1 << 3,
+};
typedef struct pcmk_child_s {
pid_t pid;
int respawn_count;
- bool respawn;
const char *name;
const char *uid;
const char *command;
const char *endpoint; /* IPC server name */
- bool needs_cluster;
int check_count;
-
- /* Anything below here will be dynamically initialized */
- bool needs_retry;
- bool active_before_startup;
+ uint32_t flags;
} pcmk_child_t;
#define PCMK_PROCESS_CHECK_INTERVAL 1
@@ -48,34 +55,34 @@ typedef struct pcmk_child_s {
static pcmk_child_t pcmk_children[] = {
{
- 0, 0, true, "pacemaker-based", CRM_DAEMON_USER,
+ 0, 0, "pacemaker-based", CRM_DAEMON_USER,
CRM_DAEMON_DIR "/pacemaker-based", PCMK__SERVER_BASED_RO,
- true
+ 0, child_respawn | child_needs_cluster
},
{
- 0, 0, true, "pacemaker-fenced", NULL,
+ 0, 0, "pacemaker-fenced", NULL,
CRM_DAEMON_DIR "/pacemaker-fenced", "stonith-ng",
- true
+ 0, child_respawn | child_needs_cluster
},
{
- 0, 0, true, "pacemaker-execd", NULL,
+ 0, 0, "pacemaker-execd", NULL,
CRM_DAEMON_DIR "/pacemaker-execd", CRM_SYSTEM_LRMD,
- false
+ 0, child_respawn
},
{
- 0, 0, true, "pacemaker-attrd", CRM_DAEMON_USER,
- CRM_DAEMON_DIR "/pacemaker-attrd", T_ATTRD,
- true
+ 0, 0, "pacemaker-attrd", CRM_DAEMON_USER,
+ CRM_DAEMON_DIR "/pacemaker-attrd", PCMK__VALUE_ATTRD,
+ 0, child_respawn | child_needs_cluster
},
{
- 0, 0, true, "pacemaker-schedulerd", CRM_DAEMON_USER,
+ 0, 0, "pacemaker-schedulerd", CRM_DAEMON_USER,
CRM_DAEMON_DIR "/pacemaker-schedulerd", CRM_SYSTEM_PENGINE,
- false
+ 0, child_respawn
},
{
- 0, 0, true, "pacemaker-controld", CRM_DAEMON_USER,
+ 0, 0, "pacemaker-controld", CRM_DAEMON_USER,
CRM_DAEMON_DIR "/pacemaker-controld", CRM_SYSTEM_CRMD,
- true
+ 0, child_respawn | child_needs_cluster
},
};
@@ -103,7 +110,7 @@ unsigned int shutdown_complete_state_reported_to = 0;
gboolean shutdown_complete_state_reported_client_closed = FALSE;
/* state we report when asked via pacemakerd-api status-ping */
-const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
+const char *pacemakerd_state = PCMK__VALUE_INIT;
gboolean running_with_sbd = FALSE; /* local copy */
GMainLoop *mainloop = NULL;
@@ -154,7 +161,7 @@ check_next_subdaemon(gpointer user_data)
pcmk_children[next_child].pid),
pcmk_children[next_child].check_count);
stop_child(&pcmk_children[next_child], SIGKILL);
- if (pcmk_children[next_child].respawn) {
+ if (pcmk_is_set(pcmk_children[next_child].flags, child_respawn)) {
/* as long as the respawn-limit isn't reached
give it another round of check retries
*/
@@ -166,7 +173,7 @@ check_next_subdaemon(gpointer user_data)
(long long) PCMK__SPECIAL_PID_AS_0(
pcmk_children[next_child].pid),
pcmk_children[next_child].check_count);
- if (pcmk_children[next_child].respawn) {
+ if (pcmk_is_set(pcmk_children[next_child].flags, child_respawn)) {
/* as long as the respawn-limit isn't reached
and we haven't run out of connect retries
we account this as progress we are willing
@@ -180,7 +187,7 @@ check_next_subdaemon(gpointer user_data)
*/
break;
case pcmk_rc_ipc_unresponsive:
- if (!pcmk_children[next_child].respawn) {
+ if (!pcmk_is_set(pcmk_children[next_child].flags, child_respawn)) {
/* if a subdaemon is down and we don't want it
to be restarted this is a success during
shutdown. if it isn't restarted anymore
@@ -191,7 +198,7 @@ check_next_subdaemon(gpointer user_data)
subdaemon_check_progress = time(NULL);
}
}
- if (!pcmk_children[next_child].active_before_startup) {
+ if (!pcmk_is_set(pcmk_children[next_child].flags, child_active_before_startup)) {
crm_trace("found %s[%lld] missing - signal-handler "
"will take care of it",
pcmk_children[next_child].name,
@@ -199,7 +206,7 @@ check_next_subdaemon(gpointer user_data)
pcmk_children[next_child].pid));
break;
}
- if (pcmk_children[next_child].respawn) {
+ if (pcmk_is_set(pcmk_children[next_child].flags, child_respawn)) {
crm_err("%s[%lld] terminated",
pcmk_children[next_child].name,
(long long) PCMK__SPECIAL_PID_AS_0(
@@ -264,14 +271,14 @@ pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitco
case CRM_EX_FATAL:
crm_warn("Shutting cluster down because %s[%d] had fatal failure",
name, pid);
- child->respawn = false;
+ child->flags &= ~child_respawn;
fatal_error = TRUE;
pcmk_shutdown(SIGTERM);
break;
case CRM_EX_PANIC:
crm_emerg("%s[%d] instructed the machine to reset", name, pid);
- child->respawn = false;
+ child->flags &= ~child_respawn;
fatal_error = TRUE;
pcmk__panic(__func__);
pcmk_shutdown(SIGTERM);
@@ -291,20 +298,20 @@ static void
pcmk_process_exit(pcmk_child_t * child)
{
child->pid = 0;
- child->active_before_startup = false;
+ child->flags &= ~child_active_before_startup;
child->check_count = 0;
child->respawn_count += 1;
if (child->respawn_count > MAX_RESPAWN) {
crm_err("Child respawn count exceeded by %s", child->name);
- child->respawn = false;
+ child->flags &= ~child_respawn;
}
if (shutdown_trigger) {
/* resume step-wise shutdown (returned TRUE yields no parallelizing) */
mainloop_set_trigger(shutdown_trigger);
- } else if (!child->respawn) {
+ } else if (!pcmk_is_set(child->flags, child_respawn)) {
/* nothing to do */
} else if (crm_is_true(pcmk__env_option(PCMK__ENV_FAIL_FAST))) {
@@ -316,10 +323,10 @@ pcmk_process_exit(pcmk_child_t * child)
" appears alright per %s IPC end-point",
child->name, child->endpoint);
- } else if (child->needs_cluster && !pcmkd_cluster_connected()) {
+ } else if (pcmk_is_set(child->flags, child_needs_cluster) && !pcmkd_cluster_connected()) {
crm_notice("Not respawning %s subdaemon until cluster returns",
child->name);
- child->needs_retry = true;
+ child->flags |= child_needs_retry;
} else {
crm_notice("Respawning %s subdaemon after unexpected exit",
@@ -336,7 +343,7 @@ pcmk_shutdown_worker(gpointer user_data)
if (phase == PCMK__NELEM(pcmk_children) - 1) {
crm_notice("Shutting down Pacemaker");
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN;
+ pacemakerd_state = PCMK__VALUE_SHUTTING_DOWN;
}
for (; phase >= 0; phase--) {
@@ -345,7 +352,7 @@ pcmk_shutdown_worker(gpointer user_data)
if (child->pid != 0) {
time_t now = time(NULL);
- if (child->respawn) {
+ if (pcmk_is_set(child->flags, child_respawn)) {
if (child->pid == PCMK__SPECIAL_PID) {
crm_warn("The process behind %s IPC cannot be"
" terminated, so either wait the graceful"
@@ -359,7 +366,7 @@ pcmk_shutdown_worker(gpointer user_data)
child->command);
}
next_log = now + 30;
- child->respawn = false;
+ child->flags &= ~child_respawn;
stop_child(child, SIGTERM);
if (phase < PCMK_CHILD_CONTROLD) {
g_timeout_add(SHUTDOWN_ESCALATION_PERIOD,
@@ -381,7 +388,7 @@ pcmk_shutdown_worker(gpointer user_data)
}
crm_notice("Shutdown complete");
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE;
+ pacemakerd_state = PCMK__VALUE_SHUTDOWN_COMPLETE;
if (!fatal_error && running_with_sbd &&
pcmk__get_sbd_sync_resource_startup() &&
!shutdown_complete_state_reported_client_closed) {
@@ -393,8 +400,12 @@ pcmk_shutdown_worker(gpointer user_data)
{
const char *delay = pcmk__env_option(PCMK__ENV_SHUTDOWN_DELAY);
if(delay) {
+ long long delay_ms = crm_get_msec(delay);
+
sync();
- pcmk__sleep_ms(crm_get_msec(delay));
+ if (delay_ms > 0) {
+ pcmk__sleep_ms((unsigned int) QB_MIN(delay_ms, UINT_MAX));
+ }
}
}
@@ -427,7 +438,7 @@ start_child(pcmk_child_t * child)
const char *env_valgrind = pcmk__env_option(PCMK__ENV_VALGRIND_ENABLED);
const char *env_callgrind = pcmk__env_option(PCMK__ENV_CALLGRIND_ENABLED);
- child->active_before_startup = false;
+ child->flags &= ~child_active_before_startup;
child->check_count = 0;
if (child->command == NULL) {
@@ -481,19 +492,20 @@ start_child(pcmk_child_t * child)
(void)setsid();
/* Setup the two alternate arg arrays */
- opts_vgrind[0] = strdup(VALGRIND_BIN);
+ opts_vgrind[0] = pcmk__str_copy(VALGRIND_BIN);
if (use_callgrind) {
- opts_vgrind[1] = strdup("--tool=callgrind");
- opts_vgrind[2] = strdup("--callgrind-out-file=" CRM_STATE_DIR "/callgrind.out.%p");
- opts_vgrind[3] = strdup(child->command);
+ opts_vgrind[1] = pcmk__str_copy("--tool=callgrind");
+ opts_vgrind[2] = pcmk__str_copy("--callgrind-out-file="
+ CRM_STATE_DIR "/callgrind.out.%p");
+ opts_vgrind[3] = pcmk__str_copy(child->command);
opts_vgrind[4] = NULL;
} else {
- opts_vgrind[1] = strdup(child->command);
+ opts_vgrind[1] = pcmk__str_copy(child->command);
opts_vgrind[2] = NULL;
opts_vgrind[3] = NULL;
opts_vgrind[4] = NULL;
}
- opts_default[0] = strdup(child->command);
+ opts_default[0] = pcmk__str_copy(child->command);
if(gid) {
// Drop root group access if not needed
@@ -759,7 +771,7 @@ find_and_track_existing_processes(void)
(long long) PCMK__SPECIAL_PID_AS_0(
pcmk_children[i].pid));
pcmk_children[i].respawn_count = -1; /* 0~keep watching */
- pcmk_children[i].active_before_startup = true;
+ pcmk_children[i].flags |= child_active_before_startup;
break;
case pcmk_rc_ipc_pid_only:
if (pcmk_children[i].respawn_count == WAIT_TRIES) {
@@ -802,7 +814,7 @@ find_and_track_existing_processes(void)
gboolean
init_children_processes(void *user_data)
{
- if (is_corosync_cluster()) {
+ if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
/* Corosync clusters can drop root group access, because we set
* uidgid.gid.${gid}=1 via CMAP, which allows these processes to connect
* to corosync.
@@ -825,8 +837,8 @@ init_children_processes(void *user_data)
*
* This may be useful for the daemons to know
*/
- pcmk__set_env_option(PCMK__ENV_RESPAWNED, "true", false);
- pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_RUNNING;
+ pcmk__set_env_option(PCMK__ENV_RESPAWNED, PCMK_VALUE_TRUE, false);
+ pacemakerd_state = PCMK__VALUE_RUNNING;
return TRUE;
}
@@ -843,13 +855,13 @@ void
restart_cluster_subdaemons(void)
{
for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
- if (!pcmk_children[i].needs_retry || pcmk_children[i].pid != 0) {
+ if (!pcmk_is_set(pcmk_children[i].flags, child_needs_retry) || pcmk_children[i].pid != 0) {
continue;
}
crm_notice("Respawning cluster-based subdaemon: %s", pcmk_children[i].name);
if (start_child(&pcmk_children[i])) {
- pcmk_children[i].needs_retry = false;
+ pcmk_children[i].flags &= ~child_needs_retry;
}
}
}
diff --git a/daemons/schedulerd/pacemaker-schedulerd.c b/daemons/schedulerd/pacemaker-schedulerd.c
index 3f2a3e8..d1b9362 100644
--- a/daemons/schedulerd/pacemaker-schedulerd.c
+++ b/daemons/schedulerd/pacemaker-schedulerd.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 @@ struct {
} options;
pcmk__output_t *logger_out = NULL;
-pcmk__output_t *out = NULL;
+static pcmk__output_t *out = NULL;
static GMainLoop *mainloop = NULL;
static qb_ipcs_service_t *ipcs = NULL;
static crm_exit_t exit_code = CRM_EX_OK;
@@ -46,6 +46,19 @@ pcmk__supported_format_t formats[] = {
void pengine_shutdown(int nsig);
+/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
+ * crm_attribute --list-options=cluster instead of querying daemon metadata.
+ */
+static int
+scheduler_metadata(pcmk__output_t *out)
+{
+ return pcmk__daemon_metadata(out, "pacemaker-schedulerd",
+ "Pacemaker scheduler options",
+ "Cluster options used by Pacemaker's "
+ "scheduler",
+ pcmk__opt_schedulerd);
+}
+
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
GOptionContext *context = NULL;
@@ -58,8 +71,7 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
{ NULL }
};
- context = pcmk__build_arg_context(args, "text (default), xml", group,
- "[metadata]");
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
pcmk__add_main_args(context, extra_prog_entries);
return context;
}
@@ -98,14 +110,20 @@ main(int argc, char **argv)
if (options.remainder) {
if (g_strv_length(options.remainder) == 1 &&
pcmk__str_eq("metadata", options.remainder[0], pcmk__str_casei)) {
- pe_metadata(out);
- goto done;
+
+ rc = scheduler_metadata(out);
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Unable to display metadata: %s", pcmk_rc_str(rc));
+ }
+
} else {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Unsupported extra command line parameters");
- goto done;
}
+ goto done;
}
if (args->version) {
diff --git a/daemons/schedulerd/pacemaker-schedulerd.h b/daemons/schedulerd/pacemaker-schedulerd.h
index 75b7d38..a7c56e1 100644
--- a/daemons/schedulerd/pacemaker-schedulerd.h
+++ b/daemons/schedulerd/pacemaker-schedulerd.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,6 @@
#include <crm/common/scheduler.h>
extern pcmk__output_t *logger_out;
-extern pcmk__output_t *out;
extern struct qb_ipcs_service_handlers ipc_callbacks;
#endif
diff --git a/daemons/schedulerd/schedulerd_messages.c b/daemons/schedulerd/schedulerd_messages.c
index 5a97365..5dcec39 100644
--- a/daemons/schedulerd/schedulerd_messages.c
+++ b/daemons/schedulerd/schedulerd_messages.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 <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <pacemaker-internal.h>
#include <stdbool.h>
@@ -27,7 +27,7 @@ init_working_set(void)
{
pcmk_scheduler_t *scheduler = pe_new_working_set();
- CRM_ASSERT(scheduler != NULL);
+ pcmk__mem_assert(scheduler);
crm_config_error = FALSE;
crm_config_warning = FALSE;
@@ -51,13 +51,14 @@ handle_pecalc_request(pcmk__request_t *request)
*/
int wrap;
} series[] = {
- { "pe-error", "pe-error-series-max", -1 },
- { "pe-warn", "pe-warn-series-max", 5000 },
- { "pe-input", "pe-input-series-max", 4000 },
+ { "pe-error", PCMK_OPT_PE_ERROR_SERIES_MAX, -1 },
+ { "pe-warn", PCMK_OPT_PE_WARN_SERIES_MAX, 5000 },
+ { "pe-input", PCMK_OPT_PE_INPUT_SERIES_MAX, 4000 },
};
xmlNode *msg = request->xml;
- xmlNode *xml_data = get_message_xml(msg, F_CRM_DATA);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL, NULL);
+ xmlNode *xml_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
static char *last_digest = NULL;
static char *filename = NULL;
@@ -75,15 +76,15 @@ handle_pecalc_request(pcmk__request_t *request)
pcmk_scheduler_t *scheduler = init_working_set();
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INDETERMINATE);
+ PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
digest = calculate_xml_versioned_digest(xml_data, FALSE, FALSE,
CRM_FEATURE_SET);
- converted = copy_xml(xml_data);
- if (!cli_config_update(&converted, NULL, TRUE)) {
- scheduler->graph = create_xml_node(NULL, XML_TAG_GRAPH);
+ converted = pcmk__xml_copy(NULL, xml_data);
+ if (pcmk_update_configured_schema(&converted, true) != pcmk_rc_ok) {
+ scheduler->graph = pcmk__xe_create(NULL, PCMK__XE_TRANSITION_GRAPH);
crm_xml_add_int(scheduler->graph, "transition_id", 0);
- crm_xml_add_int(scheduler->graph, "cluster-delay", 0);
+ crm_xml_add_int(scheduler->graph, PCMK_OPT_CLUSTER_DELAY, 0);
process = false;
free(digest);
@@ -104,15 +105,16 @@ handle_pecalc_request(pcmk__request_t *request)
}
// Get appropriate index into series[] array
- if (was_processing_error) {
+ if (was_processing_error || crm_config_error) {
series_id = 0;
- } else if (was_processing_warning) {
+ } else if (was_processing_warning || crm_config_warning) {
series_id = 1;
} else {
series_id = 2;
}
- value = pe_pref(scheduler->config_hash, series[series_id].param);
+ value = pcmk__cluster_option(scheduler->config_hash,
+ series[series_id].param);
if ((value == NULL)
|| (pcmk__scan_min_int(value, &series_wrap, -1) != pcmk_rc_ok)) {
series_wrap = series[series_id].wrap;
@@ -146,7 +148,7 @@ handle_pecalc_request(pcmk__request_t *request)
series[series_id].name, seq, true);
}
- crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename);
+ crm_xml_add(reply, PCMK__XA_CRM_TGRAPH_IN, filename);
crm_xml_add_int(reply, PCMK__XA_GRAPH_ERRORS, was_processing_error);
crm_xml_add_int(reply, PCMK__XA_GRAPH_WARNINGS, was_processing_warning);
crm_xml_add_int(reply, PCMK__XA_CONFIG_ERRORS, crm_config_error);
@@ -162,8 +164,9 @@ handle_pecalc_request(pcmk__request_t *request)
} else {
unlink(filename);
- crm_xml_add_ll(xml_data, "execution-date", (long long) execution_date);
- write_xml_file(xml_data, filename, TRUE);
+ crm_xml_add_ll(xml_data, PCMK_XA_EXECUTION_DATE,
+ (long long) execution_date);
+ pcmk__xml_write_file(xml_data, filename, true, NULL);
pcmk__write_series_sequence(PE_STATE_DIR, series[series_id].name,
++seq, series_wrap);
}
@@ -181,7 +184,7 @@ static xmlNode *
handle_unknown_request(pcmk__request_t *request)
{
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INVALID_PARAM);
+ PCMK__XE_ACK, NULL, CRM_EX_INVALID_PARAM);
pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
"Unknown IPC request type '%s' (bug?)",
@@ -193,7 +196,7 @@ static xmlNode *
handle_hello_request(pcmk__request_t *request)
{
pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
- "ack", NULL, CRM_EX_INDETERMINATE);
+ PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
crm_trace("Received IPC hello from %s", pcmk__client_name(request->ipc_client));
@@ -218,7 +221,7 @@ pe_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
crm_trace("Connection %p", c);
if (pcmk__new_client(c, uid, gid) == NULL) {
- return -EIO;
+ return -ENOMEM;
}
return 0;
}
@@ -240,19 +243,21 @@ pe_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
msg = pcmk__client_data2xml(c, data, &id, &flags);
if (msg == NULL) {
- pcmk__ipc_send_ack(c, id, flags, "ack", NULL, CRM_EX_PROTOCOL);
+ pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL, CRM_EX_PROTOCOL);
return 0;
}
- sys_to = crm_element_value(msg, F_CRM_SYS_TO);
+ sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
- if (pcmk__str_eq(crm_element_value(msg, F_CRM_MSG_TYPE),
- XML_ATTR_RESPONSE, pcmk__str_none)) {
- pcmk__ipc_send_ack(c, id, flags, "ack", NULL, CRM_EX_INDETERMINATE);
+ if (pcmk__str_eq(crm_element_value(msg, PCMK__XA_SUBT),
+ PCMK__VALUE_RESPONSE, pcmk__str_none)) {
+ pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL,
+ CRM_EX_INDETERMINATE);
crm_info("Ignoring IPC reply from %s", pcmk__client_name(c));
} else if (!pcmk__str_eq(sys_to, CRM_SYSTEM_PENGINE, pcmk__str_none)) {
- pcmk__ipc_send_ack(c, id, flags, "ack", NULL, CRM_EX_INDETERMINATE);
+ pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL,
+ CRM_EX_INDETERMINATE);
crm_info("Ignoring invalid IPC message: to '%s' not "
CRM_SYSTEM_PENGINE, pcmk__s(sys_to, ""));
@@ -271,7 +276,7 @@ pe_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
.result = PCMK__UNKNOWN_RESULT,
};
- request.op = crm_element_value_copy(request.xml, F_CRM_TASK);
+ request.op = crm_element_value_copy(request.xml, PCMK__XA_CRM_TASK);
CRM_CHECK(request.op != NULL, return 0);
reply = pcmk__process_request(&request, schedulerd_handlers);
diff --git a/devel/Makefile.am b/devel/Makefile.am
index b50f097..15012f6 100644
--- a/devel/Makefile.am
+++ b/devel/Makefile.am
@@ -165,6 +165,8 @@ coverity-clean:
## cppcheck
GLIB_CFLAGS ?= $(pkg-config --cflags glib-2.0)
+GLIB_INCL_DEF_CFLAGS = $(shell echo $(GLIB_CFLAGS) \
+ | tr ' ' '\n' | grep '^-[IDU]' | paste -d ' ')
# Use CPPCHECK_ARGS to pass extra cppcheck options, e.g.:
# --enable={warning,style,performance,portability,information,all}
@@ -181,7 +183,7 @@ cppcheck:
--output-file=$(CPPCHECK_OUT) \
--max-configs=30 --inline-suppr -q \
--library=posix --library=gnu --library=gtk \
- $(GLIB_CFLAGS) -D__GNUC__ \
+ $(GLIB_INCL_DEF_CFLAGS) -D__GNUC__ \
$(foreach dir,$(CPPCHECK_DIRS),$(top_srcdir)/$(dir))
@echo "Done: See $(CPPCHECK_OUT)"
@echo "When no longer needed, make cppcheck-clean"
diff --git a/doc/README.md b/doc/README.md
index c406fad..07f546e 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -4,13 +4,12 @@ Pacemaker has multiple forms of documentation:
* The primary end-user documentation is a series of "books":
- * Clusters From Scratch: Simplified walk-through of setting up a
- cluster for the first time
- * Pacemaker Administration: Tips for managing a cluster
- * Pacemaker Development: How to work on the Pacemaker code base
- * Pacemaker Explained: Configuration reference guide
- * Pacemaker Remote: Configuration and walk-throughs for extended
- clusters
+ * *Clusters From Scratch*: Simplified walk-through of setting up a cluster
+ for the first time
+ * *Pacemaker Administration*: Tips for managing a cluster
+ * *Pacemaker Development*: How to work on the Pacemaker code base
+ * *Pacemaker Explained*: Configuration reference guide
+ * *Pacemaker Remote*: Configuration and walk-throughs for extended clusters
The source for these is kept in this directory's sphinx subdirectory.
Generated versions are available online in epub, PDF, and HTML format at:
@@ -51,20 +50,7 @@ Pacemaker has multiple forms of documentation:
but do still have some useful information. The plan is to incorporate an
updated version of them into the books.
-## Editing the Books
+## Editing the Documentation
-The sphinx subdirectory has a subdirectory for each book by title. Each book's
-directory contains .rst files, which are the chapter sources in
-reStructuredText format (with index.rst as the starting point).
-
-Once you have edited the sources as desired, run "make" here or in the sphinx
-subdirectory to generate all the books locally. You can view the results by
-pointing your web browser to (replacing PATH\_TO\_CHECKOUT and BOOK\_TITLE
-appropriately):
-
- file:///PATH_TO_CHECKOUT/doc/sphinx/BOOK_TITLE/_build/html/index.html
-
-See the comments at the top of doc/sphinx/Makefile.am for various options you
-can use. For a guide to sphinx-flavored reStructuredText, see:
-
- https://www.sphinx-doc.org/en/master/usage/restructuredtext/
+If you wish to contribute documentation changes, please see the "Documentation"
+chapter of *Pacemaker Development*.
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index e48e19a..b95f47b 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/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.
#
@@ -84,7 +84,7 @@ EXTRA_DIST = $(wildcard */*.rst) $(DOTS) $(SVGS) \
# don't cross filesystems, sparse, show progress
RSYNC_OPTS = -rlptvzxS --progress
-PACKAGE_SERIES=$(shell echo "$VERSION" | awk -F. '{ print $1"."$2 }')
+PACKAGE_SERIES=$(shell echo "$(VERSION)" | awk -F. '{ print $$1"."$$2 }')
BOOK_RSYNC_DEST = $(RSYNC_DEST)/$(PACKAGE)/doc/$(PACKAGE_SERIES)
@@ -120,20 +120,22 @@ INKSCAPE_CMD = $(INKSCAPE) --export-dpi=90 -C
# Create the book directory in case this is a VPATH build.
$(BOOKS:%=%/conf.py): conf.py.in
$(AM_V_at)-$(MKDIR_P) "$(@:%/conf.py=%)"
- $(AM_V_GEN)sed \
- -e 's/%VERSION%/$(VERSION)/g' \
- -e 's/%BOOK_ID%/$(@:%/conf.py=%)/g' \
- -e 's/%BOOK_TITLE%/$(subst _, ,$(@:%/conf.py=%))/g' \
- -e 's#%SRC_DIR%#$(abs_srcdir)#g' \
+ $(AM_V_GEN)sed \
+ -e 's/%VERSION%/$(VERSION)/g' \
+ -e 's/%BOOK_ID%/$(@:%/conf.py=%)/g' \
+ -e 's/%BOOK_TITLE%/$(subst _, ,$(@:%/conf.py=%))/g' \
+ -e 's#%SRC_DIR%#$(abs_srcdir)#g' \
-e 's#%ABS_TOP_SRCDIR%#$(abs_top_srcdir)#g' \
-e 's#%CONFIGDIR%#@CONFIGDIR@#g' \
-e 's#%CRM_BLACKBOX_DIR%#@CRM_BLACKBOX_DIR@#g' \
+ -e 's#%CRM_CONFIG_DIR%#@CRM_CONFIG_DIR@#g' \
-e 's#%CRM_DAEMON_GROUP%#@CRM_DAEMON_GROUP@#g' \
-e 's#%CRM_DAEMON_USER%#@CRM_DAEMON_USER@#g' \
-e 's#%CRM_LOG_DIR%#@CRM_LOG_DIR@#g' \
-e 's#%CRM_SCHEMA_DIRECTORY%#@CRM_SCHEMA_DIRECTORY@#g' \
-e 's#%PACEMAKER_CONFIG_DIR%#@PACEMAKER_CONFIG_DIR@#g' \
-e 's#%PCMK_GNUTLS_PRIORITIES%#@PCMK_GNUTLS_PRIORITIES@#g' \
+ -e 's#%PCMK__REMOTE_SCHEMA_DIR%#@PCMK__REMOTE_SCHEMA_DIR@#g' \
$(<) > "$@"
$(BOOK)/_build: $(STATIC_FILES) $(BOOK)/conf.py $(DEPS_$(BOOK)) $(wildcard $(srcdir)/$(BOOK)/*.rst)
@@ -176,6 +178,15 @@ if BUILD_SPHINX_DOCS
"$(RSYNC_DEST)/$(PACKAGE)/doc"
endif
+.PHONY: vars
+vars:
+ @echo "BOOK_FORMATS='$(BOOK_FORMATS)'"
+ @echo "PAPER='$(PAPER)'"
+ @echo "SPHINXFLAGS='$(SPHINXFLAGS)'"
+ @echo "RSYNC_DEST='$(RSYNC_DEST)'"
+ @echo "VERSION='$(VERSION)'"
+ @echo "PACKAGE_SERIES='$(PACKAGE_SERIES)'"
+
.PHONY: all-local
all-local:
if BUILD_SPHINX_DOCS
diff --git a/doc/sphinx/Pacemaker_Administration/agents.rst b/doc/sphinx/Pacemaker_Administration/agents.rst
index e5b17e2..34bea60 100644
--- a/doc/sphinx/Pacemaker_Administration/agents.rst
+++ b/doc/sphinx/Pacemaker_Administration/agents.rst
@@ -53,123 +53,143 @@ _______
All OCF resource agents are required to implement the following actions.
-.. table:: **Required Actions for OCF Agents**
-
- +--------------+-------------+------------------------------------------------+
- | Action | Description | Instructions |
- +==============+=============+================================================+
- | start | Start the | .. index:: |
- | | resource | single: OCF resource agent; start |
- | | | single: start action |
- | | | |
- | | | Return 0 on success and an appropriate |
- | | | error code otherwise. Must not report |
- | | | success until the resource is fully |
- | | | active. |
- +--------------+-------------+------------------------------------------------+
- | stop | Stop the | .. index:: |
- | | resource | single: OCF resource agent; stop |
- | | | single: stop action |
- | | | |
- | | | Return 0 on success and an appropriate |
- | | | error code otherwise. Must not report |
- | | | success until the resource is fully |
- | | | stopped. |
- +--------------+-------------+------------------------------------------------+
- | monitor | Check the | .. index:: |
- | | resource's | single: OCF resource agent; monitor |
- | | state | single: monitor action |
- | | | |
- | | | Exit 0 if the resource is running, 7 |
- | | | if it is stopped, and any other OCF |
- | | | exit code if it is failed. NOTE: The |
- | | | monitor script should test the state |
- | | | of the resource on the local machine |
- | | | only. |
- +--------------+-------------+------------------------------------------------+
- | meta-data | Describe | .. index:: |
- | | the | single: OCF resource agent; meta-data |
- | | resource | single: meta-data action |
- | | | |
- | | | Provide information about this |
- | | | resource in the XML format defined by |
- | | | the OCF standard. Exit with 0. NOTE: |
- | | | This is *not* required to be performed |
- | | | as root. |
- +--------------+-------------+------------------------------------------------+
+.. list-table:: **Required Actions for OCF Agents**
+ :class: longtable
+ :widths: 1 4 3
+ :header-rows: 1
+
+ * - Action
+ - Description
+ - Instructions
+ * - .. _start_action:
+
+ .. index::
+ single: OCF resource agent; start
+ single: start action
+
+ start
+ - Start the resource
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` on success and an appropriate
+ error code otherwise. Must not report success until the resource is fully
+ active.
+ * - .. _stop_action:
+
+ .. index::
+ single: OCF resource agent; stop
+ single: stop action
+
+ stop
+ - Stop the resource
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` on success and an appropriate
+ error code otherwise. Must not report success until the resource is fully
+ stopped.
+ * - .. _monitor_action:
+
+ .. index::
+ single: OCF resource agent; monitor
+ single: monitor action
+
+ monitor
+ - Check the resource's state
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` if the resource is running,
+ :ref:`OCF_NOT_RUNNING <OCF_NOT_RUNNING>` if it is stopped, and any other
+ :ref:`OCF exit code <ocf_return_codes>` if it is failed. **Note:** The
+ monitor action should test the state of the resource on the local machine
+ only.
+ * - .. _meta_data_action:
+
+ .. index::
+ single: OCF resource agent; meta-data
+ single: meta-data action
+
+ meta-data
+ - Describe the resource
+ - Provide information about this resource in the XML format defined by the
+ OCF standard. Return :ref:`OCF_SUCCESS <OCF_SUCCESS>`. **Note:** This is
+ *not* required to be performed as root.
OCF resource agents may optionally implement additional actions. Some are used
only with advanced resource types such as clones.
-.. table:: **Optional Actions for OCF Resource Agents**
-
- +--------------+-------------+------------------------------------------------+
- | Action | Description | Instructions |
- +==============+=============+================================================+
- | validate-all | This should | .. index:: |
- | | validate | single: OCF resource agent; validate-all |
- | | the | single: validate-all action |
- | | instance | |
- | | parameters | Return 0 if parameters are valid, 2 if |
- | | provided. | not valid, and 6 if resource is not |
- | | | configured. |
- +--------------+-------------+------------------------------------------------+
- | promote | Bring the | .. index:: |
- | | local | single: OCF resource agent; promote |
- | | instance of | single: promote action |
- | | a promotable| |
- | | clone | Return 0 on success |
- | | resource to | |
- | | the promoted| |
- | | role. | |
- +--------------+-------------+------------------------------------------------+
- | demote | Bring the | .. index:: |
- | | local | single: OCF resource agent; demote |
- | | instance of | single: demote action |
- | | a promotable| |
- | | clone | Return 0 on success |
- | | resource to | |
- | | the | |
- | | unpromoted | |
- | | role. | |
- +--------------+-------------+------------------------------------------------+
- | notify | Used by the | .. index:: |
- | | cluster to | single: OCF resource agent; notify |
- | | send | single: notify action |
- | | the agent | |
- | | pre- and | Must not fail. Must exit with 0 |
- | | post- | |
- | | notification| |
- | | events | |
- | | telling the | |
- | | resource | |
- | | what has | |
- | | happened and| |
- | | will happen.| |
- +--------------+-------------+------------------------------------------------+
- | reload | Reload the | .. index:: |
- | | service's | single: OCF resource agent; reload |
- | | own | single: reload action |
- | | config. | |
- | | | Not used by Pacemaker |
- +--------------+-------------+------------------------------------------------+
- | reload-agent | Make | .. index:: |
- | | effective | single: OCF resource agent; reload-agent |
- | | any changes | single: reload-agent action |
- | | in instance | |
- | | parameters | This is used when the agent can handle a |
- | | marked as | change in some of its parameters more |
- | | reloadable | efficiently than stopping and starting the |
- | | in the | resource. |
- | | agent's | |
- | | meta-data. | |
- +--------------+-------------+------------------------------------------------+
- | recover | Restart the | .. index:: |
- | | service. | single: OCF resource agent; recover |
- | | | single: recover action |
- | | | |
- | | | Not used by Pacemaker |
- +--------------+-------------+------------------------------------------------+
+.. list-table:: **Optional Actions for OCF Resource Agents**
+ :class: longtable:
+ :widths: 1 4 3
+ :header-rows: 1
+
+ * - Action
+ - Description
+ - Instructions
+ * - .. _validate_all_action:
+
+ .. index::
+ single: OCF resource agent; validate-all
+ single: validate-all action
+
+ validate-all
+ - Validate the instance parameters provided.
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` if parameters are valid,
+ :ref:`OCF_ERR_ARGS <OCF_ERR_ARGS>` if not valid, and
+ :ref:`OCF_ERR_CONFIGURED <OCF_ERR_CONFIGURED>` if resource is not
+ configured.
+ * - .. _promote_action:
+
+ .. index::
+ single: OCF resource agent; promote
+ single: promote action
+
+ promote
+ - Bring the local instance of a promotable clone resource to the promoted
+ role.
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` on success.
+ * - .. _demote_action:
+
+ .. index::
+ single: OCF resource agent; demote
+ single: demote action
+
+ demote
+ - Bring the local instance of a promotable clone resource to the unpromoted
+ role.
+ - Return :ref:`OCF_SUCCESS <OCF_SUCCESS>` on success.
+ * - .. _notify_action:
+
+ .. index::
+ single: OCF resource agent; notify
+ single: notify action
+
+ notify
+ - Used by the cluster to send the agent pre- and post-notification events
+ telling the resource what has happened and what will happen.
+ - Must not fail. Must return :ref:`OCF_SUCCESS <OCF_SUCCESS>`.
+ * - .. _reload_action:
+
+ .. index::
+ single: OCF resource agent; reload
+ single: reload action
+
+ reload
+ - Reload the service's own configuration.
+ - Not used by Pacemaker.
+ * - .. _reload_agent_action:
+
+ .. index::
+ single: OCF resource agent; reload-agent
+ single: reload-agent action
+
+ reload-agent
+ - Make effective any changes in instance parameters marked as reloadable in
+ the agent's meta-data.
+ - This is used when the agent can handle a change in some of its parameters
+ more efficiently than stopping and starting the resource.
+ * - .. _recover_action:
+
+ .. index::
+ single: OCF resource agent; recover
+ single: recover action
+
+ recover
+ - Restart the service.
+ - Not used by Pacemaker.
.. important::
@@ -180,159 +200,214 @@ only with advanced resource types such as clones.
.. index::
single: OCF resource agent; return code
-How are OCF Return Codes Interpreted?
+How Are OCF Return Codes Interpreted?
_____________________________________
-The first thing the cluster does is to check the return code against
-the expected result. If the result does not match the expected value,
-then the operation is considered to have failed, and recovery action is
-initiated.
+The first thing the cluster does is to check the return code against the
+expected result. If the result does not match the expected value, then the
+operation is considered to have failed, and recovery action is initiated.
There are three types of failure recovery:
-.. table:: **Types of recovery performed by the cluster**
-
- +-------+--------------------------------------------+--------------------------------------+
- | Type | Description | Action Taken by the Cluster |
- +=======+============================================+======================================+
- | soft | .. index:: | Restart the resource or move it to a |
- | | single: OCF resource agent; soft error | new location |
- | | | |
- | | A transient error occurred | |
- +-------+--------------------------------------------+--------------------------------------+
- | hard | .. index:: | Move the resource elsewhere and |
- | | single: OCF resource agent; hard error | prevent it from being retried on the |
- | | | current node |
- | | A non-transient error that | |
- | | may be specific to the | |
- | | current node | |
- +-------+--------------------------------------------+--------------------------------------+
- | fatal | .. index:: | Stop the resource and prevent it |
- | | single: OCF resource agent; fatal error | from being started on any cluster |
- | | | node |
- | | A non-transient error that | |
- | | will be common to all | |
- | | cluster nodes (e.g. a bad | |
- | | configuration was specified) | |
- +-------+--------------------------------------------+--------------------------------------+
+.. list-table:: **Types of Recovery Performed by the Cluster**
+ :class: longtable
+ :widths: 1 5 5
+ :header-rows: 1
+
+ * - Type
+ - Description
+ - Action Taken by the Cluster
+ * - .. _soft_error:
+
+ .. index::
+ single: OCF resource agent; soft error
+
+ soft
+ - A transient error
+ - Restart the resource or move it to a new location
+ * - .. _hard_error:
+
+ .. index::
+ single: OCF resource agent; hard error
+
+ hard
+ - A non-transient error that may be specific to the current node
+ - Move the resource elsewhere and prevent it from being retried on the
+ current node
+ * - .. _fatal_error:
+
+ .. index::
+ single: OCF resource agent; fatal error
+
+ fatal
+ - A non-transient error that will be common to all cluster nodes (for
+ example, a bad configuration was specified)
+ - Stop the resource and prevent it from being started on any cluster node
.. _ocf_return_codes:
OCF Return Codes
________________
-The following table outlines the different OCF return codes and the type of
+The following table outlines the various OCF return codes and the type of
recovery the cluster will initiate when a failure code is received. Although
-counterintuitive, even actions that return 0 (aka. ``OCF_SUCCESS``) can be
-considered to have failed, if 0 was not the expected return value.
-
-.. table:: **OCF Exit Codes and their Recovery Types**
-
- +-------+-----------------------+---------------------------------------------------+----------+
- | Exit | OCF Alias | Description | Recovery |
- | Code | | | |
- +=======+=======================+===================================================+==========+
- | 0 | OCF_SUCCESS | .. index:: | soft |
- | | | single: OCF_SUCCESS | |
- | | | single: OCF return code; OCF_SUCCESS | |
- | | | pair: OCF return code; 0 | |
- | | | | |
- | | | Success. The command completed successfully. | |
- | | | This is the expected result for all start, | |
- | | | stop, promote and demote commands. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 1 | OCF_ERR_GENERIC | .. index:: | soft |
- | | | single: OCF_ERR_GENERIC | |
- | | | single: OCF return code; OCF_ERR_GENERIC | |
- | | | pair: OCF return code; 1 | |
- | | | | |
- | | | Generic "there was a problem" error code. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 2 | OCF_ERR_ARGS | .. index:: | hard |
- | | | single: OCF_ERR_ARGS | |
- | | | single: OCF return code; OCF_ERR_ARGS | |
- | | | pair: OCF return code; 2 | |
- | | | | |
- | | | The resource's parameter values are not valid on | |
- | | | this machine (for example, a value refers to a | |
- | | | file not found on the local host). | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 3 | OCF_ERR_UNIMPLEMENTED | .. index:: | hard |
- | | | single: OCF_ERR_UNIMPLEMENTED | |
- | | | single: OCF return code; OCF_ERR_UNIMPLEMENTED | |
- | | | pair: OCF return code; 3 | |
- | | | | |
- | | | The requested action is not implemented. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 4 | OCF_ERR_PERM | .. index:: | hard |
- | | | single: OCF_ERR_PERM | |
- | | | single: OCF return code; OCF_ERR_PERM | |
- | | | pair: OCF return code; 4 | |
- | | | | |
- | | | The resource agent does not have | |
- | | | sufficient privileges to complete the task. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 5 | OCF_ERR_INSTALLED | .. index:: | hard |
- | | | single: OCF_ERR_INSTALLED | |
- | | | single: OCF return code; OCF_ERR_INSTALLED | |
- | | | pair: OCF return code; 5 | |
- | | | | |
- | | | The tools required by the resource are | |
- | | | not installed on this machine. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 6 | OCF_ERR_CONFIGURED | .. index:: | fatal |
- | | | single: OCF_ERR_CONFIGURED | |
- | | | single: OCF return code; OCF_ERR_CONFIGURED | |
- | | | pair: OCF return code; 6 | |
- | | | | |
- | | | The resource's parameter values are inherently | |
- | | | invalid (for example, a required parameter was | |
- | | | not given). | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 7 | OCF_NOT_RUNNING | .. index:: | N/A |
- | | | single: OCF_NOT_RUNNING | |
- | | | single: OCF return code; OCF_NOT_RUNNING | |
- | | | pair: OCF return code; 7 | |
- | | | | |
- | | | The resource is safely stopped. This should only | |
- | | | be returned by monitor actions, not stop actions. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 8 | OCF_RUNNING_PROMOTED | .. index:: | soft |
- | | | single: OCF_RUNNING_PROMOTED | |
- | | | single: OCF return code; OCF_RUNNING_PROMOTED | |
- | | | pair: OCF return code; 8 | |
- | | | | |
- | | | The resource is running in the promoted role. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 9 | OCF_FAILED_PROMOTED | .. index:: | soft |
- | | | single: OCF_FAILED_PROMOTED | |
- | | | single: OCF return code; OCF_FAILED_PROMOTED | |
- | | | pair: OCF return code; 9 | |
- | | | | |
- | | | The resource is (or might be) in the promoted | |
- | | | role but has failed. The resource will be | |
- | | | demoted, stopped and then started (and possibly | |
- | | | promoted) again. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 190 | OCF_DEGRADED | .. index:: | none |
- | | | single: OCF_DEGRADED | |
- | | | single: OCF return code; OCF_DEGRADED | |
- | | | pair: OCF return code; 190 | |
- | | | | |
- | | | The resource is properly active, but in such a | |
- | | | condition that future failures are more likely. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | 191 | OCF_DEGRADED_PROMOTED | .. index:: | none |
- | | | single: OCF_DEGRADED_PROMOTED | |
- | | | single: OCF return code; OCF_DEGRADED_PROMOTED | |
- | | | pair: OCF return code; 191 | |
- | | | | |
- | | | The resource is properly active in the promoted | |
- | | | role, but in such a condition that future | |
- | | | failures are more likely. | |
- +-------+-----------------------+---------------------------------------------------+----------+
- | other | *none* | Custom error code. | soft |
- +-------+-----------------------+---------------------------------------------------+----------+
+counterintuitive, even actions that return ``OCF_SUCCESS`` can be considered to
+have failed, if ``OCF_SUCCESS`` was not the expected return value.
+
+.. list-table:: **OCF Exit Codes and Their Recovery Types**
+ :class: longtable
+ :widths: 1 3 6 2
+ :header-rows: 1
+
+ * - Exit Code
+ - OCF Alias
+ - Description
+ - Recovery
+ * - .. _OCF_SUCCESS:
+
+ .. index::
+ single: OCF_SUCCESS
+ single: OCF return code; OCF_SUCCESS
+ pair: OCF return code; 0
+
+ 0
+ - OCF_SUCCESS
+ - Success. The command completed successfully. This is the expected result
+ for all start, stop, promote, and demote actions.
+ - :ref:`soft <soft_error>`
+ * - .. _OCF_ERR_GENERIC:
+
+ .. index::
+ single: OCF_ERR_GENERIC
+ single: OCF return code; OCF_ERR_GENERIC
+ pair: OCF return code; 1
+
+ 1
+ - OCF_ERR_GENERIC
+ - Generic "there was a problem" error code.
+ - :ref:`hard <hard_error>`
+ * - .. _OCF_ERR_ARGS:
+
+ .. index::
+ single: OCF_ERR_ARGS
+ single: OCF return code; OCF_ERR_ARGS
+ pair: OCF return code; 2
+
+ 2
+ - OCF_ERR_ARGS
+ - The resource's parameter values are not valid on this machine (for
+ example, a value refers to a file not found on the local host).
+ - :ref:`hard <hard_error>`
+ * - .. _OCF_ERR_UNIMPLEMENTED:
+
+ .. index::
+ single: OCF_ERR_UNIMPLEMENTED
+ single: OCF return code; OCF_ERR_UNIMPLEMENTED
+ pair: OCF return code; 3
+
+ 3
+ - OCF_ERR_UNIMPLEMENTED
+ - The requested action is not implemented.
+ - :ref:`hard <hard_error>`
+ * - .. _OCF_ERR_PERM:
+
+ .. index::
+ single: OCF_ERR_PERM
+ single: OCF return code; OCF_ERR_PERM
+ pair: OCF return code; 4
+
+ 4
+ - OCF_ERR_PERM
+ - The resource agent does not have sufficient privileges to complete the
+ task.
+ - :ref:`hard <hard_error>`
+ * - .. _OCF_ERR_INSTALLED:
+
+ .. index::
+ single: OCF_ERR_INSTALLED
+ single: OCF return code; OCF_ERR_INSTALLED
+ pair: OCF return code; 5
+
+ 5
+ - OCF_ERR_INSTALLED
+ - The tools required by the resource are not installed on this machine.
+ - :ref:`hard <hard_error>`
+ * - .. _OCF_ERR_CONFIGURED:
+
+ .. index::
+ single: OCF_ERR_CONFIGURED
+ single: OCF return code; OCF_ERR_CONFIGURED
+ pair: OCF return code; 6
+
+ 6
+ - OCF_ERR_CONFIGURED
+ - The resource's parameter values are inherently invalid (for example, a
+ required parameter was not given).
+ - :ref:`fatal <fatal_error>`
+ * - .. _OCF_NOT_RUNNING:
+
+ .. index::
+ single: OCF_NOT_RUNNING
+ single: OCF return code; OCF_NOT_RUNNING
+ pair: OCF return code; 7
+
+ 7
+ - OCF_NOT_RUNNING
+ - The resource is safely stopped. This should only be returned by monitor
+ actions, not stop actions.
+ - N/A
+ * - .. _OCF_RUNNING_PROMOTED:
+
+ .. index::
+ single: OCF_RUNNING_PROMOTED
+ single: OCF return code; OCF_RUNNING_PROMOTED
+ pair: OCF return code; 8
+
+ 8
+ - OCF_RUNNING_PROMOTED
+ - The resource is running in the promoted role.
+ - :ref:`soft <soft_error>`
+ * - .. _OCF_FAILED_PROMOTED:
+
+ .. index::
+ single: OCF_FAILED_PROMOTED
+ single: OCF return code; OCF_FAILED_PROMOTED
+ pair: OCF return code; 9
+
+ 9
+ - OCF_FAILED_PROMOTED
+ - The resource is (or might be) in the promoted role but has failed. The
+ resource will be demoted, stopped, and then started (and possibly
+ promoted) again.
+ - :ref:`soft <soft_error>`
+ * - .. _OCF_DEGRADED:
+
+ .. index::
+ single: OCF_DEGRADED
+ single: OCF return code; OCF_DEGRADED
+ pair: OCF return code; 190
+
+ 190
+ - OCF_DEGRADED
+ - The resource is properly active, but in such a condition that future
+ failures are more likely.
+ - none
+ * - .. _OCF_DEGRADED_PROMOTED:
+
+ .. index::
+ single: OCF_DEGRADED_PROMOTED
+ single: OCF return code; OCF_DEGRADED_PROMOTED
+ pair: OCF return code; 191
+
+ 191
+ - OCF_DEGRADED_PROMOTED
+ - The resource is properly active in the promoted role, but in such a
+ condition that future failures are more likely.
+ - none
+ * - other
+ - *none*
+ - Custom error code.
+ - soft
Exceptions to the recovery handling described above:
@@ -347,6 +422,670 @@ Exceptions to the recovery handling described above:
if they had returned success, but status output will indicate that the
resource is degraded.
+.. _ocf_env_vars:
+
+Environment Variables
+_____________________
+
+Pacemaker sets certain environment variables when it executes an OCF resource
+agent. Agents can check these variables to get information about resource
+parameters or the execution environment.
+
+**Note:** Pacemaker may set other environment variables for its own purposes.
+They may be present in the agent's environment, but Pacemaker is not providing
+them for the agent's use, and so the agent should not rely on any variables not
+listed in the table below.
+
+.. list-table:: **OCF Environment Variables**
+ :class: longtable
+ :widths: 1 6
+ :header-rows: 1
+
+ * - Environment Variable
+ - Description
+ * - .. _OCF_CHECK_LEVEL:
+
+ .. index::
+ single: OCF_CHECK_LEVEL
+ single: environment variable; OCF_CHECK_LEVEL
+
+ OCF_CHECK_LEVEL
+ - Requested intensity level of checks in ``monitor`` and ``validate-all``
+ actions. Usually set as an operation attribute; see Pacemaker Explained
+ for an example.
+ * - .. _OCF_EXIT_REASON_PREFIX:
+
+ .. index::
+ single: OCF_EXIT_REASON_PREFIX
+ single: environment variable; OCF_EXIT_REASON_PREFIX
+
+ OCF_EXIT_REASON_PREFIX
+ - Prefix for printing fatal error messages from the resource agent.
+ * - .. _OCF_RA_VERSION_MAJOR:
+
+ .. index::
+ single: OCF_RA_VERSION_MAJOR
+ single: environment variable; OCF_RA_VERSION_MAJOR
+
+ OCF_RA_VERSION_MAJOR
+ - Major version number of the OCF Resource Agent API. If the script does
+ not support this revision, it should report an error.
+ See the `OCF specification <http://standards.clusterlabs.org>`_ for an
+ explanation of the versioning scheme used. The version number is split
+ into two numbers for ease of use in shell scripts. These two may be used
+ by the agent to determine whether it is run under an OCF-compliant
+ resource manager.
+ * - .. _OCF_RA_VERSION_MINOR:
+
+ .. index::
+ single: OCF_RA_VERSION_MINOR
+ single: environment variable; OCF_RA_VERSION_MINOR
+
+ OCF_RA_VERSION_MINOR
+ - Minor version number of the OCF Resource Agent API. See
+ :ref:`OCF_RA_VERSION_MAJOR <OCF_RA_VERSION_MAJOR>` for more details.
+ * - .. _OCF_RESKEY_crm_feature_set:
+
+ .. index::
+ single: OCF_RESKEY_crm_feature_set
+ single: environment variable; OCF_RESKEY_crm_feature_set
+
+ OCF_RESKEY_crm_feature_set
+ - ``crm_feature_set`` on the DC (or on the local node, if the agent is run
+ by ``crm_resource``).
+ * - .. _OCF_RESKEY_CRM_meta_interval:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_interval
+ single: environment variable; OCF_RESKEY_CRM_meta_interval
+
+ OCF_RESKEY_CRM_meta_interval
+ - Interval (in milliseconds) of the current operation.
+ * - .. _OCF_RESKEY_CRM_meta_name:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_name
+ single: environment variable; OCF_RESKEY_CRM_meta_name
+
+ OCF_RESKEY_CRM_meta_name
+ - Name of the current operation.
+ * - .. _OCF_RESKEY_CRM_meta_notify:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_notify_*
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_*
+
+ OCF_RESKEY_CRM_meta_notify_*
+ - See :ref:`Clone Notifications <clone_notifications>`.
+ * - .. _OCF_RESKEY_CRM_meta_on_node:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_on_node
+ single: environment variable; OCF_RESKEY_CRM_meta_on_node
+
+ OCF_RESKEY_CRM_meta_on_node
+ - Name of the node where the current operation is running.
+ * - .. _OCF_RESKEY_CRM_meta_on_node_uuid:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_on_node_uuid
+ single: environment variable; OCF_RESKEY_CRM_meta_on_node_uuid
+
+ OCF_RESKEY_CRM_meta_on_node_uuid
+ - Cluster-layer ID of the node where the current operation is running (or
+ node name for Pacemaker Remote nodes).
+ * - .. _OCF_RESKEY_CRM_meta_physical_host:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_physical_host
+ single: environment variable; OCF_RESKEY_CRM_meta_physical_host
+
+ OCF_RESKEY_CRM_meta_physical_host
+ - If the node where the current operation is running is a guest node, the
+ host on which the container is running.
+ * - .. _OCF_RESKEY_CRM_meta_timeout:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_timeout
+ single: environment variable; OCF_RESKEY_CRM_meta_timeout
+
+ OCF_RESKEY_CRM_meta_timeout
+ - Timeout (in milliseconds) of the current operation.
+ * - .. _OCF_RESKEY_CRM_meta:
+
+ .. index::
+ single: OCF_RESKEY_CRM_meta_*
+ single: environment variable; OCF_RESKEY_CRM_meta_*
+
+ OCF_RESKEY_CRM_meta_*
+ - Each of a resource's meta-attributes is converted to an environment
+ variable prefixed with "OCF_RESKEY_CRM_meta\_". See Pacemaker Explained
+ for some meta-attributes that have special meaning to Pacemaker.
+ * - .. _OCF_RESKEY:
+
+ .. index::
+ single: OCF_RESKEY_*
+ single: environment variable; OCF_RESKEY_*
+
+ OCF_RESKEY_*
+ - Each of a resource's instance parameters is converted to an environment
+ variable prefixed with "OCF_RESKEY\_".
+ * - .. _OCF_RESOURCE_INSTANCE:
+
+ .. index::
+ single: OCF_RESOURCE_INSTANCE
+ single: environment variable; OCF_RESOURCE_INSTANCE
+
+ OCF_RESOURCE_INSTANCE
+ - The name of the resource instance.
+ * - .. _OCF_RESOURCE_PROVIDER:
+
+ .. index::
+ single: OCF_RESOURCE_PROVIDER
+ single: environment variable; OCF_RESOURCE_PROVIDER
+
+ OCF_RESOURCE_PROVIDER
+ - The name of the resource agent provider.
+ * - .. _OCF_RESOURCE_TYPE:
+
+ .. index::
+ single: OCF_RESOURCE_TYPE
+ single: environment variable; OCF_RESOURCE_TYPE
+
+ OCF_RESOURCE_TYPE
+ - The name of the resource type.
+ * - .. _OCF_ROOT:
+
+ .. index::
+ single: OCF_ROOT
+ single: environment variable; OCF_ROOT
+
+ OCF_ROOT
+ - The root of the OCF directory hierarchy.
+ * - .. _OCF_TRACE_FILE:
+
+ .. index::
+ single: OCF_TRACE_FILE
+ single: environment variable; OCF_TRACE_FILE
+
+ OCF_TRACE_FILE
+ - The absolute path or file descriptor to write trace output to, if
+ ``OCF_TRACE_RA`` is set to true. Pacemaker sets this only to
+ ``/dev/stderr`` and only when running a resource agent via
+ ``crm_resource``.
+ * - .. _OCF_TRACE_RA:
+
+ .. index::
+ single: OCF_TRACE_RA
+ single: environment variable; OCF_TRACE_RA
+
+ OCF_TRACE_RA
+ - If set to true, enable tracing of the resource agent. Trace output is
+ written to ``OCF_TRACE_FILE`` if set; otherwise, it's written to a file
+ in ``OCF_RESKEY_trace_dir`` if set or in a default directory if not.
+ Pacemaker sets this to true only when running a resource agent via
+ ``crm_resource`` with one or more ``-V`` flags.
+ * - .. _PCMK_DEBUGLOG:
+ .. _HA_DEBUGLOG:
+
+ .. index::
+ single: PCMK_DEBUGLOG
+ single: environment variable; PCMK_DEBUGLOG
+ single: HA_DEBUGLOG
+ single: environment variable; HA_DEBUGLOG
+
+ PCMK_DEBUGLOG (and HA_DEBUGLOG)
+ - Where to write resource agent debug logs. Pacemaker sets this to
+ ``PCMK_logfile`` if set to a value other than ``none`` and if debugging
+ is enabled for the executor.
+ * - .. _PCMK_LOGFACILITY:
+ .. _HA_LOGFACILITY:
+
+ .. index::
+ single: PCMK_LOGFACILITY
+ single: environment variable; PCMK_LOGFACILITY
+ single: HA_LOGFACILITY
+ single: environment variable; HA_LOGFACILITY
+
+ PCMK_LOGFACILITY (and HA_LOGFACILITY)
+ - Syslog facility for resource agent logs. Pacemaker sets this to
+ ``PCMK_logfacility`` if set to a value other than ``none`` or
+ ``/dev/null``.
+ * - .. _PCMK_LOGFILE:
+ .. _HA_LOGFILE::
+
+ .. index::
+ single: PCMK_LOGFILE:
+ single: environment variable; PCMK_LOGFILE:
+ single: HA_LOGFILE:
+ single: environment variable; HA_LOGFILE:
+
+ PCMK_LOGFILE (and HA_LOGFILE)
+ - Where to write resource agent logs. Pacemaker sets this to
+ ``PCMK_logfile`` if set to a value other than ``none``.
+ * - .. _PCMK_service:
+
+ .. index::
+ single: PCMK_service
+ single: environment variable; PCMK_service
+
+ PCMK_service
+ - The name of the Pacemaker subsystem or command-line tool that's executing
+ the resource agent. Specific values are subject to change; useful mainly
+ for logging.
+
+Clone Resource Agent Requirements
+_________________________________
+
+Any resource can be used as an anonymous clone, as it requires no additional
+support from the resource agent. Whether it makes sense to do so depends on your
+resource and its resource agent.
+
+Resource Agent Requirements for Globally Unique Clones
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Globally unique clones require additional support in the resource agent. In
+particular, it must respond with ``OCF_SUCCESS`` only if the node has that exact
+instance active. All other probes for instances of the clone should result in
+``OCF_NOT_RUNNING`` (or one of the other OCF error codes if they are failed).
+
+Individual instances of a clone are identified by appending a colon and a
+numerical offset (for example, ``apache:2``).
+
+A resource agent can find out how many copies there are by examining the
+``OCF_RESKEY_CRM_meta_clone_max`` environment variable and which instance it is
+by examining ``OCF_RESKEY_CRM_meta_clone``.
+
+The resource agent must not make any assumptions (based on
+``OCF_RESKEY_CRM_meta_clone``) about which numerical instances are active. In
+particular, the list of active copies is not always an unbroken sequence, nor
+does it always start at 0.
+
+Resource Agent Requirements for Promotable Clones
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Promotable clone resources require two extra actions, ``demote`` and ``promote``,
+which are responsible for changing the state of the resource. Like ``start`` and
+``stop``, they should return ``OCF_SUCCESS`` if they completed successfully or a
+relevant error code if they did not.
+
+The states can mean whatever you wish, but when the resource is started, it must
+begin in the unpromoted role. From there, the cluster will decide which
+instances to promote.
+
+In addition to the clone requirements for monitor actions, agents must also
+*accurately* report which state they are in. The cluster relies on the agent to
+report its status (including role) accurately and does not indicate to the agent
+what role it currently believes it to be in.
+
+.. list-table:: **Role Implications of OCF Return Codes**
+ :class: longtable
+ :widths: 1 3
+ :header-rows: 1
+
+ * - Monitor Return Code
+ - Description
+ * - :ref:`OCF_NOT_RUNNING <OCF_NOT_RUNNING>`
+ - .. index::
+ single: OCF_NOT_RUNNING
+ single: OCF return code; OCF_NOT_RUNNING
+
+ Stopped
+ * - :ref:`OCF_SUCCESS <OCF_SUCCESS>`
+ - .. index::
+ single: OCF_SUCCESS
+ single: OCF return code; OCF_SUCCESS
+
+ Running (Unpromoted)
+ * - :ref:`OCF_RUNNING_PROMOTED <OCF_RUNNING_PROMOTED>`
+ - .. index::
+ single: OCF_RUNNING_PROMOTED
+ single: OCF return code; OCF_RUNNING_PROMOTED
+
+ Running (Promoted)
+ * - :ref:`OCF_FAILED_PROMOTED <OCF_FAILED_PROMOTED>`
+ - .. index::
+ single: OCF_FAILED_PROMOTED
+ single: OCF return code; OCF_FAILED_PROMOTED
+
+ Failed (Promoted)
+ * - Other
+ - Failed (Unpromoted)
+
+.. _clone_notifications:
+
+Clone Notifications
+~~~~~~~~~~~~~~~~~~~
+
+If the clone has the ``notify`` meta-attribute set to ``true`` and the resource
+agent supports the ``notify`` action, Pacemaker will call the action when
+appropriate, passing a number of extra variables. These variables, when combined
+with additional context, can be used to calculate the current state of the
+cluster and what is about to happen to it.
+
+.. index::
+ single: clone; environment variables
+ single: notify; environment variables
+
+.. list-table:: **Environment Variables Supplied with Clone Notify Actions**
+ :class: longtable
+ :widths: 1 1
+ :header-rows: 1
+
+ * - Variable
+ - Description
+ * - .. _OCF_RESKEY_CRM_meta_notify_type:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_type
+ single: OCF_RESKEY_CRM_meta_notify_type
+
+ OCF_RESKEY_CRM_meta_notify_type
+ - Allowed values: ``pre``, ``post``
+ * - .. _OCF_RESKEY_CRM_meta_notify_operation:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_operation
+ single: OCF_RESKEY_CRM_meta_notify_operation
+
+ OCF_RESKEY_CRM_meta_notify_operation
+ - Allowed values: ``start``, ``stop``
+ * - .. _OCF_RESKEY_CRM_meta_notify_start_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_start_resource
+ single: OCF_RESKEY_CRM_meta_notify_start_resource
+
+ OCF_RESKEY_CRM_meta_notify_start_resource
+ - Resources to be started
+ * - .. _OCF_RESKEY_CRM_meta_notify_stop_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_stop_resource
+ single: OCF_RESKEY_CRM_meta_notify_stop_resource
+
+ OCF_RESKEY_CRM_meta_notify_stop_resource
+ - Resources to be stopped
+ * - .. _OCF_RESKEY_CRM_meta_notify_active_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_active_resource
+ single: OCF_RESKEY_CRM_meta_notify_active_resource
+
+ OCF_RESKEY_CRM_meta_notify_active_resource
+ - Resources that are running
+ * - .. _OCF_RESKEY_CRM_meta_notify_inactive_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_inactive_resource
+ single: OCF_RESKEY_CRM_meta_notify_inactive_resource
+
+ OCF_RESKEY_CRM_meta_notify_inactive_resource
+ - Resources that are not running
+ * - .. _OCF_RESKEY_CRM_meta_notify_start_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_start_uname
+ single: OCF_RESKEY_CRM_meta_notify_start_uname
+
+ OCF_RESKEY_CRM_meta_notify_start_uname
+ - Nodes on which resources will be started
+ * - .. _OCF_RESKEY_CRM_meta_notify_stop_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_stop_uname
+ single: OCF_RESKEY_CRM_meta_notify_stop_uname
+
+ OCF_RESKEY_CRM_meta_notify_stop_uname
+ - Nodes on which resources will be stopped
+ * - .. _OCF_RESKEY_CRM_meta_notify_active_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_active_uname
+ single: OCF_RESKEY_CRM_meta_notify_active_uname
+
+ OCF_RESKEY_CRM_meta_notify_active_uname
+ - Nodes on which resources are running
+
+The variables come in pairs, such as
+``OCF_RESKEY_CRM_meta_notify_start_resource`` and
+``OCF_RESKEY_CRM_meta_notify_start_uname``, and should be treated as an array of
+whitespace-separated elements.
+
+``OCF_RESKEY_CRM_meta_notify_inactive_resource`` is an exception, as the
+matching ``uname`` variable does not exist since inactive resources are not
+running on any node.
+
+Thus, in order to indicate that ``clone:0`` will be started on ``sles-1``,
+``clone:2`` will be started on ``sles-3``, and ``clone:3`` will be started
+on ``sles-2``, the cluster would set:
+
+.. topic:: Notification Variables
+
+ .. code-block:: none
+
+ OCF_RESKEY_CRM_meta_notify_start_resource="clone:0 clone:2 clone:3"
+ OCF_RESKEY_CRM_meta_notify_start_uname="sles-1 sles-3 sles-2"
+
+.. note::
+
+ Pacemaker will log but otherwise ignore failures of notify actions.
+
+Interpretation of Notification Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Pre-notification (stop):**
+
+* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+**Post-notification (stop) / Pre-notification (start):**
+
+* Active resources
+ * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Inactive resources
+ * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+**Post-notification (start):**
+
+* Active resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Inactive resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+Extra Notifications for Promotable Clones
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. index::
+ single: clone; environment variables
+ single: promotable; environment variables
+
+.. list-table:: **Extra Environment Variables Supplied for Promotable Clones**
+ :class: longtable
+ :widths: 1 1
+ :header-rows: 1
+
+ * - Variable
+ - Description
+ * - .. _OCF_RESKEY_CRM_meta_notify_promoted_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_promoted_resource
+ single: OCF_RESKEY_CRM_meta_notify_promoted_resource
+
+ OCF_RESKEY_CRM_meta_notify_promoted_resource
+ - Resources that are running in the promoted role
+ * - .. _OCF_RESKEY_CRM_meta_notify_unpromoted_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_unpromoted_resource
+ single: OCF_RESKEY_CRM_meta_notify_unpromoted_resource
+
+ OCF_RESKEY_CRM_meta_notify_unpromoted_resource
+ - Resources that are running in the unpromoted role
+ * - .. _OCF_RESKEY_CRM_meta_notify_promote_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_promote_resource
+ single: OCF_RESKEY_CRM_meta_notify_promote_resource
+
+ OCF_RESKEY_CRM_meta_notify_promote_resource
+ - Resources to be promoted
+ * - .. _OCF_RESKEY_CRM_meta_notify_demote_resource:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_demote_resource
+ single: OCF_RESKEY_CRM_meta_notify_demote_resource
+
+ OCF_RESKEY_CRM_meta_notify_demote_resource
+ - Resources to be demoted
+ * - .. _OCF_RESKEY_CRM_meta_notify_promote_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_promote_uname
+ single: OCF_RESKEY_CRM_meta_notify_promote_uname
+
+ OCF_RESKEY_CRM_meta_notify_promote_uname
+ - Nodes on which resources will be promoted
+ * - .. _OCF_RESKEY_CRM_meta_notify_demote_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_demote_uname
+ single: OCF_RESKEY_CRM_meta_notify_demote_uname
+
+ OCF_RESKEY_CRM_meta_notify_demote_uname
+ - Nodes on which resources will be demoted
+ * - .. _OCF_RESKEY_CRM_meta_notify_promoted_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_promoted_uname
+ single: OCF_RESKEY_CRM_meta_notify_promoted_uname
+
+ OCF_RESKEY_CRM_meta_notify_promoted_uname
+ - Nodes on which resources are running in the promoted role
+ * - .. _OCF_RESKEY_CRM_meta_notify_unpromoted_uname:
+
+ .. index::
+ single: environment variable; OCF_RESKEY_CRM_meta_notify_unpromoted_uname
+ single: OCF_RESKEY_CRM_meta_notify_unpromoted_uname
+
+ OCF_RESKEY_CRM_meta_notify_unpromoted_uname
+ - Nodes on which resources are running in the unpromoted role
+
+Interpretation of Promotable Notification Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Pre-notification (demote):**
+
+* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+* Promoted resources: ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
+* Unpromoted resources: ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
+* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+**Post-notification (demote) / Pre-notification (stop):**
+
+* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+* Promoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Unpromoted resources: ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
+* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+
+**Post-notification (stop) / Pre-notification (start)**
+
+* Active resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Promoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Unpromoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Inactive resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+**Post-notification (start) / Pre-notification (promote)**
+
+* Active resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Promoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Unpromoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Inactive resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
+**Post-notification (promote)**
+
+* Active resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Promoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Unpromoted resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Inactive resources:
+ * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
+ * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+ * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
+* Resources that were promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
+* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
+* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
+
.. index::
single: resource agent; LSB
diff --git a/doc/sphinx/Pacemaker_Administration/configuring.rst b/doc/sphinx/Pacemaker_Administration/configuring.rst
index 295c96a..e4d70c4 100644
--- a/doc/sphinx/Pacemaker_Administration/configuring.rst
+++ b/doc/sphinx/Pacemaker_Administration/configuring.rst
@@ -186,53 +186,14 @@ Connecting from a Remote Machine
Provided Pacemaker is installed on a machine, it is possible to connect to the
cluster even if the machine itself is not in the same cluster. To do this, one
-simply sets up a number of environment variables and runs the same commands as
-when working on a cluster node.
-
-.. list-table:: **Environment Variables Used to Connect to Remote Instances of the CIB**
- :class: longtable
- :widths: 2 2 5
- :header-rows: 1
-
- * - Environment Variable
- - Default
- - Description
- * - .. index::
- single: CIB_user
- single: environment variable; CIB_user
-
- CIB_user
- - |CRM_DAEMON_USER_RAW|
- - The user to connect as. Needs to be part of the |CRM_DAEMON_GROUP| group
- on the target host.
- * - .. index::
- single: CIB_passwd
- single: environment variable; CIB_passwd
-
- CIB_passwd
- -
- - The user's password. Read from the command line if unset.
- * - .. index::
- single: CIB_server
- single: environment variable; CIB_server
-
- CIB_server
- - localhost
- - The host to contact
- * - .. index::
- single: CIB_port
- single: environment variable; CIB_port
-
- CIB_port
- -
- - The port on which to contact the server; required
- * - .. index::
- single: CIB_encrypted
- single: environment variable; CIB_encrypted
-
- CIB_encrypted
- - true
- - Whether to encrypt network traffic
+simply sets the following environment variables and runs the same commands as
+when working on a cluster node:
+
+* :ref:`CIB_port <CIB_port>` (required)
+* :ref:`CIB_server <CIB_server>`
+* :ref:`CIB_user <CIB_user>`
+* :ref:`CIB_passwd <CIB_passwd>`
+* :ref:`CIB_encrypted <CIB_encrypted>`
So, if **c001n01** is an active cluster node and is listening on port 1234
for connections, and **someuser** is a member of the |CRM_DAEMON_GROUP| group,
diff --git a/doc/sphinx/Pacemaker_Administration/index.rst b/doc/sphinx/Pacemaker_Administration/index.rst
index af89380..c8fd722 100644
--- a/doc/sphinx/Pacemaker_Administration/index.rst
+++ b/doc/sphinx/Pacemaker_Administration/index.rst
@@ -20,6 +20,7 @@ Table of Contents
intro
installing
cluster
+ options
configuring
tools
administrative
diff --git a/doc/sphinx/Pacemaker_Administration/installing.rst b/doc/sphinx/Pacemaker_Administration/installing.rst
index 44a3f5f..feea962 100644
--- a/doc/sphinx/Pacemaker_Administration/installing.rst
+++ b/doc/sphinx/Pacemaker_Administration/installing.rst
@@ -4,6 +4,6 @@ Installing Cluster Software
.. index:: installation
Most major Linux distributions have pacemaker packages in their standard
-package repositories, or the software can be built from source code.
-See the `Install wiki page <https://wiki.clusterlabs.org/wiki/Install>`_
-for details.
+package repositories, or the software can be built from source code. See
+`How to Install <https://projects.clusterlabs.org/w/cluster_administration/how_to_install/>`_
+on the ClusterLabs wiki for details.
diff --git a/doc/sphinx/Pacemaker_Administration/options.rst b/doc/sphinx/Pacemaker_Administration/options.rst
new file mode 100644
index 0000000..731d17f
--- /dev/null
+++ b/doc/sphinx/Pacemaker_Administration/options.rst
@@ -0,0 +1,178 @@
+.. index:: client options
+
+Client Options
+--------------
+
+Pacemaker uses several environment variables set on the client side.
+
+.. note:: Directory and file paths below may differ on your system depending on
+ your Pacemaker build settings. Check your Pacemaker configuration
+ file to find the correct paths.
+
+.. list-table:: **Client-side Environment Variables**
+ :class: longtable
+ :widths: 2 4 5
+ :header-rows: 1
+
+ * - Environment Variable
+ - Default
+ - Description
+ * - .. _CIB_encrypted:
+
+ .. index::
+ single: CIB_encrypted
+ single: environment variable; CIB_encrypted
+
+ CIB_encrypted
+ - true
+ - Whether to encrypt network traffic. Used with :ref:`CIB_port <CIB_port>`
+ for connecting to a remote CIB instance; ignored if
+ :ref:`CIB_port <CIB_port>` is not set.
+ * - .. _CIB_file:
+
+ .. index::
+ single: CIB_file
+ single: environment variable; CIB_file
+
+ CIB_file
+ -
+ - If set, CIB connections are created against the named XML file. Clients
+ read an input CIB from, and write the result CIB to, the named file.
+ Ignored if :ref:`CIB_shadow <CIB_shadow>` is set.
+ * - .. _CIB_passwd:
+
+ .. index::
+ single: CIB_passwd
+ single: environment variable; CIB_passwd
+
+ CIB_passwd
+ -
+ - :ref:`$CIB_user <CIB_user>`'s password. Read from the command line if
+ unset. Used with :ref:`CIB_port <CIB_port>` for connecting to a remote
+ CIB instance; ignored if :ref:`CIB_port <CIB_port>` is not set.
+ * - .. _CIB_port:
+
+ .. index::
+ single: CIB_port
+ single: environment variable; CIB_port
+
+ CIB_port
+ -
+ - If set, CIB connections are created as clients to a remote CIB instance
+ on :ref:`$CIB_server <CIB_server>` via this port. Ignored if
+ :ref:`CIB_shadow <CIB_shadow>` or :ref:`CIB_file <CIB_file>` is set.
+ * - .. _CIB_server:
+
+ .. index::
+ single: CIB_server
+ single: environment variable; CIB_server
+
+ CIB_server
+ - localhost
+ - The host to connect to. Used with :ref:`CIB_port <CIB_port>` for
+ connecting to a remote CIB instance; ignored if
+ :ref:`CIB_port <CIB_port>` is not set.
+ * - .. _CIB_shadow:
+
+ .. index::
+ single: CIB_shadow
+ single: environment variable; CIB_shadow
+
+ CIB_shadow
+ -
+ - If set, CIB connections are created against a temporary working
+ ("shadow") CIB file called ``shadow.$CIB_shadow`` in
+ :ref:`$CIB_shadow_dir <CIB_shadow_dir>`. Should be set only to the name
+ of a shadow CIB created by :ref:`crm_shadow <crm_shadow>`. Otherwise,
+ behavior is undefined.
+ * - .. _CIB_shadow_dir:
+
+ .. index::
+ single: CIB_shadow_dir
+ single: environment variable; CIB_shadow_dir
+
+ CIB_shadow_dir
+ - |CRM_CONFIG_DIR| if the current user is ``root`` or |CRM_DAEMON_USER|;
+ otherwise ``$HOME/.cib`` if :ref:`$HOME <HOME>` is set; otherwise
+ ``$TMPDIR/.cib`` if :ref:`$TMPDIR <TMPDIR>` is set to an absolute path;
+ otherwise ``/tmp/.cib``
+ - If set, shadow files are created in this directory. Ignored if
+ :ref:`CIB_shadow <CIB_shadow>` is not set.
+ * - .. _CIB_user:
+
+ .. index::
+ single: CIB_user
+ single: environment variable; CIB_user
+
+ CIB_user
+ - |CRM_DAEMON_USER| if used with :ref:`CIB_port <CIB_port>`, or the current
+ effective user otherwise
+ - If used with :ref:`CIB_port <CIB_port>`, connect to
+ :ref:`$CIB_server <CIB_server>` as this user. Must be part of the
+ |CRM_DAEMON_GROUP| group on :ref:`$CIB_server <CIB_server>`. Otherwise
+ (without :ref:`CIB_port <CIB_port>`), this is used only for ACL and
+ display purposes.
+ * - .. _EDITOR:
+
+ .. index::
+ single: EDITOR
+ single: environment variable; EDITOR
+
+ EDITOR
+ -
+ - Text editor to use for editing shadow files. Required for the ``--edit``
+ command of :ref:`crm_shadow <crm_shadow>`.
+ * - .. _HOME:
+
+ .. index::
+ single: HOME
+ single: environment variable; HOME
+
+ HOME
+ - Current user's home directory as configured in the passwd database, if an
+ entry exists
+ - Used to create a default :ref:`CIB_shadow_dir <CIB_shadow_dir>` for non-
+ privileged users.
+ * - .. _PE_fail:
+
+ .. index::
+ single: PE_fail
+ single: environment variable; PE_fail
+
+ PE_fail
+ - 0
+ - Advanced use only: A dummy graph action with action ID matching this
+ option will be marked as failed. Primarily for developer use with
+ scheduler simulations.
+ * - .. _PS1:
+
+ .. index::
+ single: PS1
+ single: environment variable; PS1
+
+ PS1
+ -
+ - The shell's primary prompt string. Used by
+ :ref:`crm_shadow <crm_shadow>`: set to indicate that the user is in an
+ interactive shadow CIB session, and checked to determine whether the user
+ is already in an interactive session before creating a new one.
+ * - .. _SHELL:
+
+ .. index::
+ single: SHELL
+ single: environment variable; SHELL
+
+ SHELL
+ -
+ - Absolute path to a shell. Used by :ref:`crm_shadow <crm_shadow>` when
+ launching an interactive session.
+ * - .. _TMPDIR:
+
+ .. index::
+ single: TMPDIR
+ single: environment variable; TMPDIR
+
+ TMPDIR
+ - /tmp
+ - Directory for temporary files. If not an absolute path, the default is
+ used instead.
diff --git a/doc/sphinx/Pacemaker_Administration/pcs-crmsh.rst b/doc/sphinx/Pacemaker_Administration/pcs-crmsh.rst
index 3eda60a..06fb24f 100644
--- a/doc/sphinx/Pacemaker_Administration/pcs-crmsh.rst
+++ b/doc/sphinx/Pacemaker_Administration/pcs-crmsh.rst
@@ -4,7 +4,7 @@ Quick Comparison of pcs and crm shell
``pcs`` and ``crm shell`` are two popular higher-level command-line interfaces
to Pacemaker. Each has its own syntax; this chapter gives a quick comparion of
how to accomplish the same tasks using either one. Some examples also show the
-equivalent command using low-level Pacmaker command-line tools.
+equivalent command using low-level Pacemaker command-line tools.
These examples show the simplest syntax; see the respective man pages for all
possible options.
@@ -118,6 +118,7 @@ Manage Resources
.. topic:: Create a Resource
.. code-block:: none
+
crmsh # crm configure primitive ClusterIP IPaddr2 params ip=192.168.122.120 cidr_netmask=24
pcs # pcs resource create ClusterIP IPaddr2 ip=192.168.122.120 cidr_netmask=24
diff --git a/doc/sphinx/Pacemaker_Administration/upgrading.rst b/doc/sphinx/Pacemaker_Administration/upgrading.rst
index 1ca2a4e..bccfc22 100644
--- a/doc/sphinx/Pacemaker_Administration/upgrading.rst
+++ b/doc/sphinx/Pacemaker_Administration/upgrading.rst
@@ -159,11 +159,12 @@ Special considerations when planning a rolling upgrade:
* If the Pacemaker Remote protocol version is changing, all cluster nodes
should be upgraded before upgrading any Pacemaker Remote nodes.
-See the ClusterLabs wiki's
-`release calendar <https://wiki.clusterlabs.org/wiki/ReleaseCalendar>`_
-to figure out whether the CRM feature set and/or Pacemaker Remote protocol
-version changed between the the Pacemaker release versions in your rolling
-upgrade.
+See the
+`Pacemaker release calendar
+<https://projects.clusterlabs.org/w/projects/pacemaker/pacemaker_release_calendar/>`_
+on the ClusterLabs wiki to figure out whether the CRM feature set and/or
+Pacemaker Remote protocol version changed between the the Pacemaker release
+versions in your rolling upgrade.
To perform a rolling upgrade, on each node in turn:
@@ -302,9 +303,8 @@ A more cautious approach would proceed like this:
#. The transformation was successful but produced an invalid result.
If the result of the transformation is invalid, you may see a number of
- errors from the validation library. If these are not helpful, visit the
- `Validation FAQ wiki page <https://wiki.clusterlabs.org/wiki/Validation_FAQ>`_
- and/or try the manual upgrade procedure described below.
+ errors from the validation library. If these are not helpful, try the manual
+ upgrade procedure described below.
#. Check the changes:
@@ -398,9 +398,10 @@ the C API. Highlights:
higher-level tools are strongly recommended to use instead of trying to parse
the text output, which may change from release to release).
-For a detailed list of changes, see the release notes and the
-`Pacemaker 2.1 Changes <https://wiki.clusterlabs.org/wiki/Pacemaker_2.1_Changes>`_
-page on the ClusterLabs wiki.
+For a detailed list of changes, see the release notes and
+`Pacemaker 2.1 Changes
+<https://projects.clusterlabs.org/w/projects/pacemaker/pacemaker_2.1_changes/>`_
+on the ClusterLabs wiki.
What Changed in 2.0
@@ -431,9 +432,10 @@ behavior. Highlights:
* The public API for Pacemaker libraries that software applications can use
has changed significantly.
-For a detailed list of changes, see the release notes and the
-`Pacemaker 2.0 Changes <https://wiki.clusterlabs.org/wiki/Pacemaker_2.0_Changes>`_
-page on the ClusterLabs wiki.
+For a detailed list of changes, see the release notes and
+`Pacemaker 2.0 Changes
+<https://projects.clusterlabs.org/w/projects/pacemaker/pacemaker_2.0_changes/>`_
+on the ClusterLabs wiki.
What Changed in 1.0
diff --git a/doc/sphinx/Pacemaker_Development/c.rst b/doc/sphinx/Pacemaker_Development/c.rst
index b03ddae..8bc5e80 100644
--- a/doc/sphinx/Pacemaker_Development/c.rst
+++ b/doc/sphinx/Pacemaker_Development/c.rst
@@ -752,12 +752,35 @@ Function names should be unique across the entire project, to allow for
individual tracing via ``PCMK_trace_functions``, and make it easier to search
code and follow detail logs.
-A common function signature is a comparison function that returns 0 if its
-arguments are equal for sorting purposes, -1 if the first argument should sort
-first, and 1 is the second argument should sort first. Such a function should
-have ``cmp`` in its name, to parallel ``strcmp()``; ``sort`` should only be
-used in the names of functions that sort an entire list (typically using a
-``cmp`` function).
+.. _sort_func:
+
+Sorting
+^^^^^^^
+
+A function that sorts an entire list should have ``sort`` in its name. It sorts
+elements using a :ref:`comparison <compare_func>` function, which may be either
+hard-coded or passed as an argument.
+
+.. _compare_func:
+
+Comparison
+^^^^^^^^^^
+
+A comparison function for :ref:`sorting <sort_func>` should have ``cmp`` in its
+name and should *not* have ``sort`` in its name.
+
+.. _constructor_func:
+
+Constructors
+^^^^^^^^^^^^
+
+A constructor creates a new dynamically allocated object. It may perform some
+initialization procedure on the new object.
+
+* If the constructor always creates an independent object instance, its name
+ should include ``new``.
+* If the constructor may add the new object to some existing object, its name
+ should include ``create``.
Function Definitions
@@ -832,6 +855,12 @@ messages and converting from one to another, can be found in
Of course, functions may have return values that aren't success/failure
indicators, such as a pointer, integer count, or bool.
+:ref:`Comparison <compare_func>` functions should return
+
+* a negative integer if the first argument should sort first
+* 0 if its arguments are equal for sorting purposes
+* a positive integer is the second argument should sort first
+
Public API Functions
____________________
@@ -880,6 +909,30 @@ __________________________________
* The convenience macros ``pcmk__plural_s()`` and ``pcmk__plural_alt()`` are
handy when logging a word that may be singular or plural.
+Log Levels
+__________
+
+When to use each log level:
+
+* **critical:** fatal error (usually something that would make a daemon exit)
+* **error:** failure of something that affects the cluster (such as a resource
+ action, fencing action, etc.) or daemon operation
+* **warning:** minor, potential, or recoverable failures (such as something
+ only affecting a daemon client, or invalid configuration that can be left to
+ default)
+* **notice:** important successful events (such as a node joining or leaving,
+ resource action results, or configuration changes)
+* **info:** events that would be helpful with troubleshooting (such as status
+ section updates or elections)
+* **debug:** information that would be helpful for debugging code or complex
+ problems
+* **trace:** like debug but for very noisy or low-level stuff
+
+By default, critical through notice are logged to the system log and detail
+log, info is logged to the detail log only, and debug and trace are not logged
+(if enabled, they go to the detail log only).
+
+
Logging
_______
@@ -912,6 +965,34 @@ using libqb's "extended logging" feature:
pcmk_rc_str(rc), rc, id);
+Assertion Logging
+_________________
+
+``CRM_ASSERT(expr)``
+ If ``expr`` is false, this will call <code>crm_err()</code> with a "Triggered
+ fatal assert" message (with details), then abort execution. This should be
+ used for logic errors that should be impossible (such as a NULL function
+ argument where not accepted) and environmental errors that can't be handled
+ gracefully (for example, memory allocation failures, though returning
+ ``ENOMEM`` is often better).
+
+``CRM_LOG_ASSERT(expr)``
+ If ``expr`` is false, this will generally log a message without aborting. If
+ the log level is below trace, it just calls ``crm_err()`` with a "Triggered
+ assert" message (with details). If the log level is trace, and the caller is
+ a daemon, then it will fork a child process in which to dump core, as well as
+ logging the message. If the log level is trace, and the caller is not a
+ daemon, then it will behave like ``CRM_ASSERT()`` (i.e. log and abort). This
+ should be used for logic or protocol errors that require no special handling.
+
+``CRM_CHECK(expr, failed_action)``
+ If ``expr`` is false, behave like ``CRM_LOG_ASSERT(expr)`` (that is, log a
+ message and dump core if requested) then perform ``failed_action`` (which
+ must not contain ``continue``, ``break``, or ``errno``). This should be used
+ for logic or protocol errors that can be handled, usually by returning an
+ error status.
+
+
Output
______
@@ -924,12 +1005,40 @@ A custom message can be defined with a unique string identifier, plus
implementation functions for each supported format. The caller invokes the
message using the identifier. The user selects the output format via
``--output-as``, and the output code automatically calls the appropriate
-implementation function.
+implementation function. Custom messages are useful when you want to output
+messages that are more complex than a one-line error or informational message,
+reproducible, and automatically handled by the output formatting system.
+Custom messages can contain other custom messages.
+
+Custom message functions are implemented as follows: Start with the macro
+``PCMK__OUTPUT_ARGS``, whose arguments are the message name, followed by the
+arguments to the message. Then there is the function declaration, for which the
+arguments are the pointer to the current output object, then a variable argument
+list.
+
+To output a custom message, you first need to create, i.e. register, the custom
+message that you want to output. Either call ``register_message``, which
+registers a custom message at runtime, or make use of the collection of
+predefined custom messages in ``fmt_functions``, which is defined in
+``lib/pacemaker/pcmk_output.c``. Once you have the message to be outputted,
+output it by calling ``message``.
+
+Note: The ``fmt_functions`` functions accommodate all of the output formats;
+the default implementation accommodates any format that isn't explicitly
+accommodated. The default output provides valid output for any output format,
+but you may still want to implement a specific output, i.e. xml, text, or html.
+The ``message`` function automatically knows which implementation to use,
+because the ``pcmk__output_s`` contains this information.
The interface (most importantly ``pcmk__output_t``) is declared in
``include/crm/common/output*h``. See the API comments and existing tools for
-examples.
+examples.
+Some of its important member functions are ``err``, which formats error messages
+and ``info``, which formats informational messages. Also, ``list_item``,
+which formats list items, ``begin_list``, which starts lists, and ``end_list``,
+which ends lists, are important because lists can be useful, yet differently
+handled by the different output types.
.. index::
single: Makefile.am
diff --git a/doc/sphinx/Pacemaker_Development/components.rst b/doc/sphinx/Pacemaker_Development/components.rst
index 5086fa8..ce6b36b 100644
--- a/doc/sphinx/Pacemaker_Development/components.rst
+++ b/doc/sphinx/Pacemaker_Development/components.rst
@@ -27,10 +27,10 @@ As might be expected, it has the most code of any of the daemons.
Join sequence
_____________
-Most daemons track their cluster peers using Corosync's membership and CPG
-only. The controller additionally requires peers to `join`, which ensures they
-are ready to be assigned tasks. Joining proceeds through a series of phases
-referred to as the `join sequence` or `join process`.
+Most daemons track their cluster peers using Corosync's membership and
+:term:`CPG` only. The controller additionally requires peers to `join`, which
+ensures they are ready to be assigned tasks. Joining proceeds through a series
+of phases referred to as the `join sequence` or `join process`.
A node's current join phase is tracked by the ``join`` member of ``crm_node_t``
(used in the peer cache). It is an ``enum crm_join_phase`` that (ideally)
@@ -141,7 +141,7 @@ _______________
The function calls for a fencing request go something like this:
-The local fencer receives the client's request via an IPC or messaging
+The local fencer receives the client's request via an :term:`IPC` or messaging
layer callback, which calls
* ``stonith_command()``, which (for requests) calls
@@ -199,8 +199,8 @@ __________________
Each ``STONITH_OP_FENCE`` request goes something like this:
-The chosen peer fencer receives the ``STONITH_OP_FENCE`` request via IPC or
-messaging layer callback, which calls:
+The chosen peer fencer receives the ``STONITH_OP_FENCE`` request via
+:term:`IPC` or messaging layer callback, which calls:
* ``stonith_command()``, which (for requests) calls
@@ -240,7 +240,7 @@ returns, and calls
Fencing replies
_______________
-The original fencer receives the ``STONITH_OP_FENCE`` reply via IPC or
+The original fencer receives the ``STONITH_OP_FENCE`` reply via :term:`IPC` or
messaging layer callback, which calls:
* ``stonith_command()``, which (for replies) calls
@@ -295,10 +295,10 @@ The purpose of the scheduler is to take a CIB as input and generate a
transition graph (list of actions that need to be taken) as output.
The controller invokes the scheduler by contacting the scheduler daemon via
-local IPC. Tools such as ``crm_simulate``, ``crm_mon``, and ``crm_resource``
-can also invoke the scheduler, but do so by calling the library functions
-directly. This allows them to run using a ``CIB_file`` without the cluster
-needing to be active.
+local :term:`IPC`. Tools such as ``crm_simulate``, ``crm_mon``, and
+``crm_resource`` can also invoke the scheduler, but do so by calling the
+library functions directly. This allows them to run using a ``CIB_file``
+without the cluster needing to be active.
The main entry point for the scheduler code is
``lib/pacemaker/pcmk_scheduler.c:pcmk__schedule_actions()``. It sets
@@ -315,7 +315,7 @@ defaults and calls a series of functions for the scheduling. Some key steps:
the CIB status section. This is used to decide whether certain
actions need to be done, such as deleting orphan resources, forcing a restart
when a resource definition changes, etc.
-* ``assign_resources()`` assigns resources to nodes.
+* ``assign_resources()`` :term:`assigns <assign>` resources to nodes.
* ``schedule_resource_actions()`` schedules resource-specific actions (which
might or might not end up in the final graph).
* ``pcmk__apply_orderings()`` processes ordering constraints in order to modify
@@ -364,7 +364,7 @@ Resources
_________
``pcmk_resource_t`` is the data object representing cluster resources. A
-resource has a variant: primitive (a.k.a. native), group, clone, or bundle.
+resource has a variant: :term:`primitive`, group, clone, or :term:`bundle`.
The resource object has members for two sets of methods,
``resource_object_functions_t`` from the ``libpe_status`` public API, and
@@ -374,9 +374,9 @@ The resource object has members for two sets of methods,
The object functions have basic capabilities such as unpacking the resource
XML, and determining the current or planned location of the resource.
-The assignment functions have more obscure capabilities needed for scheduling,
-such as processing location and ordering constraints. For example,
-``pcmk__create_internal_constraints()`` simply calls the
+The :term:`assignment <assign>` functions have more obscure capabilities needed
+for scheduling, such as processing location and ordering constraints. For
+example, ``pcmk__create_internal_constraints()`` simply calls the
``internal_constraints()`` method for each top-level resource in the cluster.
.. index::
@@ -385,9 +385,10 @@ such as processing location and ordering constraints. For example,
Nodes
_____
-Assignment of resources to nodes is done by choosing the node with the highest
-score for a given resource. The scheduler does a bunch of processing to
-generate the scores, then the actual assignment is straightforward.
+:term:`Assignment <assign>` of resources to nodes is done by choosing the node
+with the highest :term:`score` for a given resource. The scheduler does a bunch
+of processing to generate the scores, then the actual assignment is
+straightforward.
Node lists are frequently used. For example, ``pcmk_scheduler_t`` has a
``nodes`` member which is a list of all nodes in the cluster, and
@@ -435,8 +436,8 @@ ___________
Colocation constraints come into play in these parts of the scheduler code:
-* When sorting resources for assignment, so resources with highest node score
- are assigned first (see ``cmp_resources()``)
+* When sorting resources for :term:`assignment <assign>`, so resources with
+ highest node :term:`score` are assigned first (see ``cmp_resources()``)
* When updating node scores for resource assigment or promotion priority
* When assigning resources, so any resources to be colocated with can be
assigned first, and so colocations affect where the resource is assigned
@@ -449,7 +450,8 @@ The resource assignment functions have several methods related to colocations:
dependent's allowed node scores (if called while resources are being
assigned) or the dependent's priority (if called while choosing promotable
instance roles). It can behave differently depending on whether it is being
- called as the primary's method or as the dependent's method.
+ called as the :term:`primary's <primary>` method or as the :term:`dependent's
+ <dependent>` method.
* ``add_colocated_node_scores():`` This updates a table of nodes for a given
colocation attribute and score. It goes through colocations involving a given
resource, and updates the scores of the nodes in the table with the best
diff --git a/doc/sphinx/Pacemaker_Development/documentation.rst b/doc/sphinx/Pacemaker_Development/documentation.rst
new file mode 100644
index 0000000..6880bb0
--- /dev/null
+++ b/doc/sphinx/Pacemaker_Development/documentation.rst
@@ -0,0 +1,35 @@
+.. index::
+ pair: documentation; guidelines
+
+Documentation Guidelines
+------------------------
+
+See `doc/README.md
+<https://github.com/ClusterLabs/pacemaker/blob/main/doc/README.md>`_ in the
+source code repository for the kinds of documentation that Pacemaker provides.
+
+Books
+#####
+
+The ``doc/sphinx`` subdirectory has a subdirectory for each book by title. Each
+book's directory contains .rst files, which are the chapter sources in
+`reStructuredText
+<https://www.sphinx-doc.org/en/master/usage/restructuredtext/>`_ format (with
+index.rst as the starting point).
+
+Once you have edited the sources as desired, run ``make`` in the ``doc`` or
+``doc/sphinx`` directory to generate all the books locally. You can view the
+results by pointing your web browser to (replacing PATH\_TO\_CHECKOUT and
+BOOK\_TITLE appropriately):
+
+ file:///PATH_TO_CHECKOUT/doc/sphinx/BOOK_TITLE/_build/html/index.html
+
+See the comments at the top of ``doc/sphinx/Makefile.am`` for options you can
+use.
+
+Recommended practices:
+
+* Use ``list-table`` instead of ``table`` for tables
+* When documenting newly added features and syntax, add "\*(since X.Y.Z)\*"
+ with the version introducing them. These comments can be removed when rolling
+ upgrades from that version are no longer supported.
diff --git a/doc/sphinx/Pacemaker_Development/faq.rst b/doc/sphinx/Pacemaker_Development/faq.rst
index e738b7d..b1b1e5a 100644
--- a/doc/sphinx/Pacemaker_Development/faq.rst
+++ b/doc/sphinx/Pacemaker_Development/faq.rst
@@ -32,21 +32,20 @@ Frequently Asked Questions
:Q: What are the different Git branches and repositories used for?
:A: * The `main branch <https://github.com/ClusterLabs/pacemaker/tree/main>`_
- is the primary branch used for development.
- * The `2.1 branch <https://github.com/ClusterLabs/pacemaker/tree/2.1>`_ is
- the current release branch. Normally, it does not receive any changes, but
- during the release cycle for a new release, it will contain release
- candidates. During the release cycle, certain bug fixes will go to the
- 2.1 branch first (and be pulled into main later).
+ is used for all new development.
+ * The `3.0 <https://github.com/ClusterLabs/pacemaker/tree/3.0>`_ and
+ `2.1 <https://github.com/ClusterLabs/pacemaker/tree/2.1>`_ branches are
+ for the currently supported major and minor version release series.
+ Normally, they do not receive any changes, but during the release cycle
+ for a new release, they will contain release candidates. The main branch
+ is pulled into 3.0 just before the first release candidate of a new
+ release, but otherwise, separate pull requests must be submitted to
+ backport changes from the main branch into a release branch.
* The `2.0 branch <https://github.com/ClusterLabs/pacemaker/tree/2.0>`_,
`1.1 branch <https://github.com/ClusterLabs/pacemaker/tree/1.1>`_,
and separate
`1.0 repository <https://github.com/ClusterLabs/pacemaker-1.0>`_
are frozen snapshots of earlier release series, no longer being developed.
- * Messages will be posted to the
- `developers@ClusterLabs.org <https://lists.ClusterLabs.org/mailman/listinfo/developers>`_
- mailing list during the release cycle, with instructions about which
- branches to use when submitting requests.
----
@@ -163,9 +162,5 @@ Frequently Asked Questions
:Q: What if I still have questions?
:A: Ask on the
- `developers@ClusterLabs.org <https://lists.ClusterLabs.org/mailman/listinfo/developers>`_
- mailing list for development-related questions, or on the
- `users@ClusterLabs.org <https://lists.ClusterLabs.org/mailman/listinfo/users>`_
- mailing list for general questions about using Pacemaker.
- Developers often also hang out on the
- [ClusterLabs IRC channel](https://wiki.clusterlabs.org/wiki/ClusterLabs_IRC_channel).
+ `ClusterLabs mailing lists
+ <https://projects.clusterlabs.org/w/clusterlabs/clusterlabs_mailing_lists/>`_.
diff --git a/doc/sphinx/Pacemaker_Development/general.rst b/doc/sphinx/Pacemaker_Development/general.rst
index 9d9dcec..94015c9 100644
--- a/doc/sphinx/Pacemaker_Development/general.rst
+++ b/doc/sphinx/Pacemaker_Development/general.rst
@@ -38,3 +38,13 @@ may put more specific copyright notices in their commit messages if desired.
`"Updating Copyright Notices"
<https://techwhirl.com/updating-copyright-notices/>`_ for a more
readable summary.
+
+Terminology
+###########
+
+Pacemaker is extremely complex, and it helps to use terminology consistently
+throughout documentation, symbol names and comments in code, and so forth. It
+also helps to use natural language when practical instead of technical jargon
+and acronyms.
+
+For specific recommendations, see the :ref:`glossary`.
diff --git a/doc/sphinx/Pacemaker_Development/glossary.rst b/doc/sphinx/Pacemaker_Development/glossary.rst
new file mode 100644
index 0000000..6f73e96
--- /dev/null
+++ b/doc/sphinx/Pacemaker_Development/glossary.rst
@@ -0,0 +1,84 @@
+.. index::
+ single: glossary
+
+.. _glossary:
+
+Glossary
+--------
+
+.. glossary::
+
+ assign
+ In the scheduler, this refers to associating a resource with a node. Do
+ not use *allocate* for this purpose.
+
+ bundle
+ The collective resource type associating instances of a container with
+ storage and networking. Do not use :term:`container` when referring to
+ the bundle as a whole.
+
+ cluster layer
+ The layer of the :term:`cluster stack` that provides membership and
+ messaging capabilities (such as Corosync).
+
+ cluster stack
+ The core components of a high-availability cluster: the
+ :term:`cluster layer` at the "bottom" of the stack, then Pacemaker, then
+ resource agents, and then the actual services managed by the cluster at
+ the "top" of the stack. Do not use *stack* for the cluster layer alone.
+
+ CPG
+ Corosync Process Group. This is the messaging layer in a Corosync-based
+ cluster. Pacemaker daemons use CPG to communicate with their counterparts
+ on other nodes.
+
+ container
+ This can mean either a container in the usual sense (whether as a
+ standalone resource or as part of a bundle), or as the container resource
+ meta-attribute (which does not necessarily reference a container in the
+ usual sense).
+
+ dangling migration
+ Live migration of a resource consists of a **migrate_to** action on the
+ source node, followed by a **migrate_from** on the target node, followed
+ by a **stop** on the source node. If the **migrate_to** and
+ **migrate_from** have completed successfully, but the **stop** has not
+ yet been done, the migration is considered to be *dangling*.
+
+ dependent
+ In colocation constraints, this refers to the resource located relative
+ to the :term:`primary` resource. Do not use *rh* or *right-hand* for this
+ purpose.
+
+ IPC
+ Inter-process communication. In Pacemaker, clients send requests to
+ daemons using libqb IPC.
+
+ message
+ This can refer to log messages, custom messages defined for a
+ **pcmk_output_t** object, or XML messages sent via :term:`CPG` or
+ :term:`IPC`.
+
+ metadata
+ In the context of options and resource agents, this refers to OCF-style
+ metadata. Do not use a hyphen except when referring to the OCF-defined
+ action name *meta-data*.
+
+ primary
+ In colocation constraints, this refers to the resource that the
+ :term:`dependent` resource is located relative to. Do not use *lh* or
+ *left-hand* for this purpose.
+
+ primitive
+ The fundamental resource type in Pacemaker. Do not use *native* for this
+ purpose.
+
+ score
+ An integer value constrained between **-PCMK_SCORE_INFINITY** and
+ **+PCMK_SCORE_INFINITY**. Certain strings (such as
+ **PCMK_VALUE_INFINITY**) parse as particular score values. Do not use
+ *weight* for this purpose.
+
+ self-fencing
+ When a node is chosen to execute its own fencing. Do not use *suicide*
+ for this purpose.
diff --git a/doc/sphinx/Pacemaker_Development/index.rst b/doc/sphinx/Pacemaker_Development/index.rst
index cbe1499..a3f624f 100644
--- a/doc/sphinx/Pacemaker_Development/index.rst
+++ b/doc/sphinx/Pacemaker_Development/index.rst
@@ -20,11 +20,13 @@ Table of Contents
faq
general
+ documentation
python
c
components
helpers
evolution
+ glossary
Index
-----
diff --git a/doc/sphinx/Pacemaker_Explained/alerts.rst b/doc/sphinx/Pacemaker_Explained/alerts.rst
index 1d02187..f4cad72 100644
--- a/doc/sphinx/Pacemaker_Explained/alerts.rst
+++ b/doc/sphinx/Pacemaker_Explained/alerts.rst
@@ -1,3 +1,5 @@
+.. _alerts:
+
.. index::
single: alert
single: resource; alert
@@ -209,7 +211,28 @@ By default, an alert agent will be called for node events, fencing events, and
resource events. An agent may choose to ignore certain types of events, but
there is still the overhead of calling it for those events. To eliminate that
overhead, you may select which types of events the agent should receive.
-
+
+Alert filters are configured within a ``select`` element inside an ``alert``
+element.
+
+.. list-table:: **Possible alert filters**
+ :class: longtable
+ :widths: 1 3
+ :header-rows: 1
+
+ * - Name
+ - Events alerted
+ * - select_nodes
+ - A node joins or leaves the cluster (whether at the cluster layer for
+ cluster nodes, or via a remote connection for Pacemaker Remote nodes).
+ * - select_fencing
+ - Fencing or unfencing of a node completes (whether successfully or not).
+ * - select_resources
+ - A resource action other than meta-data completes (whether successfully
+ or not).
+ * - select_attributes
+ - A transient attribute value update is sent to the CIB.
+
.. topic:: Alert configuration to receive only node events and fencing events
.. code-block:: xml
@@ -227,9 +250,6 @@ overhead, you may select which types of events the agent should receive.
</alerts>
</configuration>
-The possible options within ``<select>`` are ``<select_nodes>``,
-``<select_fencing>``, ``<select_resources>``, and ``<select_attributes>``.
-
With ``<select_attributes>`` (the only event type not enabled by default), the
agent will receive alerts when a node attribute changes. If you wish the agent
to be called only when certain attributes change, you can configure that as well.
diff --git a/doc/sphinx/Pacemaker_Explained/cluster-options.rst b/doc/sphinx/Pacemaker_Explained/cluster-options.rst
index 77bd7e6..042ed0b 100644
--- a/doc/sphinx/Pacemaker_Explained/cluster-options.rst
+++ b/doc/sphinx/Pacemaker_Explained/cluster-options.rst
@@ -62,143 +62,6 @@ Normally, you will use command-line tools that abstract the XML, so the
distinction will be unimportant; both properties and options are cluster
settings you can tweak.
-Configuration Value Types
-#########################
-
-Throughout this document, configuration values will be designated as having one
-of the following types:
-
-.. list-table:: **Configuration Value Types**
- :class: longtable
- :widths: 1 3
- :header-rows: 1
-
- * - Type
- - Description
- * - .. _boolean:
-
- .. index::
- pair: type; boolean
-
- boolean
- - Case-insensitive text value where ``1``, ``yes``, ``y``, ``on``,
- and ``true`` evaluate as true and ``0``, ``no``, ``n``, ``off``,
- ``false``, and unset evaluate as false
- * - .. _date_time:
-
- .. index::
- pair: type; date/time
-
- date/time
- - Textual timestamp like ``Sat Dec 21 11:47:45 2013``
- * - .. _duration:
-
- .. index::
- pair: type; duration
-
- duration
- - A time duration, specified either like a :ref:`timeout <timeout>` or an
- `ISO 8601 duration <https://en.wikipedia.org/wiki/ISO_8601#Durations>`_.
- A duration may be up to approximately 49 days but is intended for much
- smaller time periods.
- * - .. _enumeration:
-
- .. index::
- pair: type; enumeration
-
- enumeration
- - Text that must be one of a set of defined values (which will be listed
- in the description)
- * - .. _integer:
-
- .. index::
- pair: type; integer
-
- integer
- - 32-bit signed integer value (-2,147,483,648 to 2,147,483,647)
- * - .. _nonnegative_integer:
-
- .. index::
- pair: type; nonnegative integer
-
- nonnegative integer
- - 32-bit nonnegative integer value (0 to 2,147,483,647)
- * - .. _port:
-
- .. index::
- pair: type; port
-
- port
- - Integer TCP port number (0 to 65535)
- * - .. _score:
-
- .. index::
- pair: type; score
-
- score
- - A Pacemaker score can be an integer between -1,000,000 and 1,000,000, or
- a string alias: ``INFINITY`` or ``+INFINITY`` is equivalent to
- 1,000,000, ``-INFINITY`` is equivalent to -1,000,000, and ``red``,
- ``yellow``, and ``green`` are equivalent to integers as described in
- :ref:`node-health`.
- * - .. _text:
-
- .. index::
- pair: type; text
-
- text
- - A text string
- * - .. _timeout:
-
- .. index::
- pair: type; timeout
-
- timeout
- - A time duration, specified as a bare number (in which case it is
- considered to be in seconds) or a number with a unit (``ms`` or ``msec``
- for milliseconds, ``us`` or ``usec`` for microseconds, ``s`` or ``sec``
- for seconds, ``m`` or ``min`` for minutes, ``h`` or ``hr`` for hours)
- optionally with whitespace before and/or after the number.
- * - .. _version:
-
- .. index::
- pair: type; version
-
- version
- - Version number (any combination of alphanumeric characters, dots, and
- dashes, starting with a number).
-
-
-Scores
-______
-
-Scores are integral to how Pacemaker works. Practically everything from moving
-a resource to deciding which resource to stop in a degraded cluster is achieved
-by manipulating scores in some way.
-
-Scores are calculated per resource and node. Any node with a negative score for
-a resource can't run that resource. The cluster places a resource on the node
-with the highest score for it.
-
-Score addition and subtraction follow these rules:
-
-* Any value (including ``INFINITY``) - ``INFINITY`` = ``-INFINITY``
-* ``INFINITY`` + any value other than ``-INFINITY`` = ``INFINITY``
-
-.. note::
-
- What if you want to use a score higher than 1,000,000? Typically this possibility
- arises when someone wants to base the score on some external metric that might
- go above 1,000,000.
-
- The short answer is you can't.
-
- The long answer is it is sometimes possible work around this limitation
- creatively. You may be able to set the score to some computed value based on
- the external metric rather than use the metric directly. For nodes, you can
- store the metric as a node attribute, and query the attribute when computing
- the score (possibly as part of a custom resource agent).
-
CIB Properties
##############
@@ -321,6 +184,15 @@ holds. So the decision was made to place them in an easy-to-find location.
-
- Node ID of the cluster's current designated controller (DC). Used and
maintained by the cluster.
+ * - .. _execution_date:
+
+ .. index::
+ pair: execution-date; cib
+
+ execution-date
+ - :ref:`epoch time <epoch_time>`
+ -
+ - Time to use when evaluating rules.
.. _cluster_options:
@@ -427,6 +299,29 @@ values, by running the ``man pacemaker-schedulerd`` and
- The number of :ref:`live migration <live-migration>` actions that the
cluster is allowed to execute in parallel on a node. A value of -1 means
unlimited.
+ * - .. _load_threshold:
+
+ .. index::
+ pair: cluster option; load-threshold
+
+ load-threshold
+ - :ref:`percentage <percentage>`
+ - 80%
+ - Maximum amount of system load that should be used by cluster nodes. The
+ cluster will slow down its recovery process when the amount of system
+ resources used (currently CPU) approaches this limit.
+ * - .. _node_action_limit:
+
+ .. index::
+ pair: cluster option; node-action-limit
+
+ node-action-limit
+ - :ref:`integer <integer>`
+ - 0
+ - Maximum number of jobs that can be scheduled per node. If nonpositive or
+ invalid, double the number of cores is used as the maximum number of jobs
+ per node. :ref:`PCMK_node_action_limit <pcmk_node_action_limit>`
+ overrides this option on a per-node basis.
* - .. _symmetric_cluster:
.. index::
@@ -558,6 +453,22 @@ values, by running the ``man pacemaker-schedulerd`` and
- How many times fencing can fail for a target before the cluster will no
longer immediately re-attempt it. Any value below 1 will be ignored, and
the default will be used instead.
+ * - .. _have_watchdog:
+
+ .. index::
+ pair: cluster option; have-watchdog
+
+ have-watchdog
+ - :ref:`boolean <boolean>`
+ - *detected*
+ - Whether watchdog integration is enabled. 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
+ :ref:`stonith-watchdog-timeout <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.
* - .. _stonith_watchdog_timeout:
.. index::
@@ -568,23 +479,29 @@ values, by running the ``man pacemaker-schedulerd`` and
- 0
- If nonzero, and the cluster detects ``have-watchdog`` as ``true``, then
watchdog-based self-fencing will be performed via SBD when fencing is
- required, without requiring a fencing resource explicitly configured.
-
- If this is set to a positive value, unseen nodes are assumed to
- self-fence within this much time.
+ required.
- **Warning:** It must be ensured that this value is larger than the
- ``SBD_WATCHDOG_TIMEOUT`` environment variable on all nodes. Pacemaker
- verifies the settings individually on all nodes and prevents startup or
- shuts down if configured wrongly on the fly. It is strongly recommended
- that ``SBD_WATCHDOG_TIMEOUT`` be set to the same value on all nodes.
+ If this is set to a positive value, lost nodes are assumed to achieve
+ self-fencing 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.
- If this is set to a negative value, and ``SBD_WATCHDOG_TIMEOUT`` is set,
- twice that value will be used.
+ **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.
- **Warning:** In this case, it is essential (and currently not verified
- by pacemaker) that ``SBD_WATCHDOG_TIMEOUT`` is set to the same value on
- all nodes.
* - .. _concurrent-fencing:
.. index::
@@ -607,12 +524,13 @@ values, by running the ``man pacemaker-schedulerd`` and
- :ref:`enumeration <enumeration>`
- stop
- How should a cluster node react if notified of its own fencing? A
- cluster node may receive notification of its own fencing if fencing is
- misconfigured, or if fabric fencing is in use that doesn't cut cluster
- communication. Allowed values are ``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. The default is
- likely to be changed to ``panic`` in a future release. *(since 2.0.3)*
+ 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. Allowed values are ``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. The default is likely to be changed to ``panic`` in a future
+ release. *(since 2.0.3)*
* - .. _priority_fencing_delay:
.. index::
@@ -784,7 +702,7 @@ values, by running the ``man pacemaker-schedulerd`` and
node-health-red
- :ref:`score <score>`
- - 0
+ - -INFINITY
- The score to use for a node health attribute whose value is ``red``.
Only used when ``node-health-strategy`` is ``progressive`` or
``custom``.
@@ -797,10 +715,10 @@ values, by running the ``man pacemaker-schedulerd`` and
- :ref:`duration <duration>`
- 15min
- Pacemaker is primarily event-driven, and looks ahead to know when to
- recheck the cluster for failure timeouts and most time-based rules
- *(since 2.0.3)*. However, it will also recheck the cluster after this
- amount of inactivity. This has two goals: rules with ``date_spec`` are
- only guaranteed to be checked this often, and it also serves as a
+ recheck the cluster for failure-timeout settings and most time-based
+ rules *(since 2.0.3)*. However, it will also recheck the cluster after
+ this amount of inactivity. This has two goals: rules with ``date_spec``
+ are only guaranteed to be checked this often, and it also serves as a
fail-safe for some kinds of scheduler bugs. A value of 0 disables this
polling.
* - .. _shutdown_lock:
diff --git a/doc/sphinx/Pacemaker_Explained/collective.rst b/doc/sphinx/Pacemaker_Explained/collective.rst
index a4fa9dc..dc6832c 100644
--- a/doc/sphinx/Pacemaker_Explained/collective.rst
+++ b/doc/sphinx/Pacemaker_Explained/collective.rst
@@ -569,418 +569,7 @@ instances around the cluster.
apply to clone instances as well. This means an explicit ``resource-stickiness``
of 0 in ``rsc_defaults`` works differently from the implicit default used when
``resource-stickiness`` is not specified.
-
-Clone Resource Agent Requirements
-_________________________________
-
-Any resource can be used as an anonymous clone, as it requires no
-additional support from the resource agent. Whether it makes sense to
-do so depends on your resource and its resource agent.
-
-Resource Agent Requirements for Globally Unique Clones
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Globally unique clones require additional support in the resource agent. In
-particular, it must only respond with ``${OCF_SUCCESS}`` if the node has that
-exact instance active. All other probes for instances of the clone should
-result in ``${OCF_NOT_RUNNING}`` (or one of the other OCF error codes if
-they are failed).
-
-Individual instances of a clone are identified by appending a colon and a
-numerical offset, e.g. **apache:2**.
-
-Resource agents can find out how many copies there are by examining
-the ``OCF_RESKEY_CRM_meta_clone_max`` environment variable and which
-instance it is by examining ``OCF_RESKEY_CRM_meta_clone``.
-
-The resource agent must not make any assumptions (based on
-``OCF_RESKEY_CRM_meta_clone``) about which numerical instances are active. In
-particular, the list of active copies will not always be an unbroken
-sequence, nor always start at 0.
-
-Resource Agent Requirements for Promotable Clones
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Promotable clone resources require two extra actions, ``demote`` and ``promote``,
-which are responsible for changing the state of the resource. Like **start** and
-**stop**, they should return ``${OCF_SUCCESS}`` if they completed successfully or
-a relevant error code if they did not.
-
-The states can mean whatever you wish, but when the resource is
-started, it must come up in the unpromoted role. From there, the
-cluster will decide which instances to promote.
-
-In addition to the clone requirements for monitor actions, agents must
-also *accurately* report which state they are in. The cluster relies
-on the agent to report its status (including role) accurately and does
-not indicate to the agent what role it currently believes it to be in.
-
-.. table:: **Role implications of OCF return codes**
- :widths: 1 3
-
- +----------------------+--------------------------------------------------+
- | Monitor Return Code | Description |
- +======================+==================================================+
- | OCF_NOT_RUNNING | .. index:: |
- | | single: OCF_NOT_RUNNING |
- | | single: OCF return code; OCF_NOT_RUNNING |
- | | |
- | | Stopped |
- +----------------------+--------------------------------------------------+
- | OCF_SUCCESS | .. index:: |
- | | single: OCF_SUCCESS |
- | | single: OCF return code; OCF_SUCCESS |
- | | |
- | | Running (Unpromoted) |
- +----------------------+--------------------------------------------------+
- | OCF_RUNNING_PROMOTED | .. index:: |
- | | single: OCF_RUNNING_PROMOTED |
- | | single: OCF return code; OCF_RUNNING_PROMOTED |
- | | |
- | | Running (Promoted) |
- +----------------------+--------------------------------------------------+
- | OCF_FAILED_PROMOTED | .. index:: |
- | | single: OCF_FAILED_PROMOTED |
- | | single: OCF return code; OCF_FAILED_PROMOTED |
- | | |
- | | Failed (Promoted) |
- +----------------------+--------------------------------------------------+
- | Other | .. index:: |
- | | single: return code |
- | | |
- | | Failed (Unpromoted) |
- +----------------------+--------------------------------------------------+
-
-Clone Notifications
-~~~~~~~~~~~~~~~~~~~
-
-If the clone has the ``notify`` meta-attribute set to **true**, and the resource
-agent supports the ``notify`` action, Pacemaker will call the action when
-appropriate, passing a number of extra variables which, when combined with
-additional context, can be used to calculate the current state of the cluster
-and what is about to happen to it.
-
-.. index::
- single: clone; environment variables
- single: notify; environment variables
-
-.. table:: **Environment variables supplied with Clone notify actions**
- :widths: 1 1
-
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | Variable | Description |
- +==============================================+===============================================================================+
- | OCF_RESKEY_CRM_meta_notify_type | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_type |
- | | single: OCF_RESKEY_CRM_meta_notify_type |
- | | |
- | | Allowed values: **pre**, **post** |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_operation | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_operation |
- | | single: OCF_RESKEY_CRM_meta_notify_operation |
- | | |
- | | Allowed values: **start**, **stop** |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_start_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_start_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_start_resource |
- | | |
- | | Resources to be started |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_stop_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_stop_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_stop_resource |
- | | |
- | | Resources to be stopped |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_active_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_active_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_active_resource |
- | | |
- | | Resources that are running |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_inactive_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_inactive_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_inactive_resource |
- | | |
- | | Resources that are not running |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_start_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_start_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_start_uname |
- | | |
- | | Nodes on which resources will be started |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_stop_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_stop_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_stop_uname |
- | | |
- | | Nodes on which resources will be stopped |
- +----------------------------------------------+-------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_active_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_active_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_active_uname |
- | | |
- | | Nodes on which resources are running |
- +----------------------------------------------+-------------------------------------------------------------------------------+
-
-The variables come in pairs, such as
-``OCF_RESKEY_CRM_meta_notify_start_resource`` and
-``OCF_RESKEY_CRM_meta_notify_start_uname``, and should be treated as an
-array of whitespace-separated elements.
-
-``OCF_RESKEY_CRM_meta_notify_inactive_resource`` is an exception, as the
-matching **uname** variable does not exist since inactive resources
-are not running on any node.
-
-Thus, in order to indicate that **clone:0** will be started on **sles-1**,
-**clone:2** will be started on **sles-3**, and **clone:3** will be started
-on **sles-2**, the cluster would set:
-
-.. topic:: Notification variables
-
- .. code-block:: none
-
- OCF_RESKEY_CRM_meta_notify_start_resource="clone:0 clone:2 clone:3"
- OCF_RESKEY_CRM_meta_notify_start_uname="sles-1 sles-3 sles-2"
-
-.. note::
-
- Pacemaker will log but otherwise ignore failures of notify actions.
-
-Interpretation of Notification Variables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-**Pre-notification (stop):**
-
-* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
-* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-**Post-notification (stop) / Pre-notification (start):**
-
-* Active resources
-
- * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-* Inactive resources
-
- * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-**Post-notification (start):**
-
-* Active resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Inactive resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-Extra Notifications for Promotable Clones
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. index::
- single: clone; environment variables
- single: promotable; environment variables
-
-.. table:: **Extra environment variables supplied for promotable clones**
- :widths: 1 1
-
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | Variable | Description |
- +================================================+=================================================================================+
- | OCF_RESKEY_CRM_meta_notify_promoted_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_promoted_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_promoted_resource |
- | | |
- | | Resources that are running in the promoted role |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_unpromoted_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_unpromoted_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_unpromoted_resource |
- | | |
- | | Resources that are running in the unpromoted role |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_promote_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_promote_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_promote_resource |
- | | |
- | | Resources to be promoted |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_demote_resource | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_demote_resource |
- | | single: OCF_RESKEY_CRM_meta_notify_demote_resource |
- | | |
- | | Resources to be demoted |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_promote_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_promote_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_promote_uname |
- | | |
- | | Nodes on which resources will be promoted |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_demote_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_demote_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_demote_uname |
- | | |
- | | Nodes on which resources will be demoted |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_promoted_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_promoted_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_promoted_uname |
- | | |
- | | Nodes on which resources are running in the promoted role |
- +------------------------------------------------+---------------------------------------------------------------------------------+
- | OCF_RESKEY_CRM_meta_notify_unpromoted_uname | .. index:: |
- | | single: environment variable; OCF_RESKEY_CRM_meta_notify_unpromoted_uname |
- | | single: OCF_RESKEY_CRM_meta_notify_unpromoted_uname |
- | | |
- | | Nodes on which resources are running in the unpromoted role |
- +------------------------------------------------+---------------------------------------------------------------------------------+
-
-Interpretation of Promotable Notification Variables
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-**Pre-notification (demote):**
-
-* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
-* Promoted resources: ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
-* Unpromoted resources: ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
-* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-**Post-notification (demote) / Pre-notification (stop):**
-
-* Active resources: ``$OCF_RESKEY_CRM_meta_notify_active_resource``
-* Promoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-
-* Unpromoted resources: ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
-* Inactive resources: ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-
-**Post-notification (stop) / Pre-notification (start)**
-
-* Active resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-* Promoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-
-* Unpromoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-* Inactive resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-**Post-notification (start) / Pre-notification (promote)**
-
-* Active resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Promoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-
-* Unpromoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Inactive resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
-**Post-notification (promote)**
-
-* Active resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_active_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Promoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_promoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-
-* Unpromoted resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_unpromoted_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-
-* Inactive resources:
-
- * ``$OCF_RESKEY_CRM_meta_notify_inactive_resource``
- * plus ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
- * minus ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-
-* Resources to be started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources to be promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources to be demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources to be stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-* Resources that were started: ``$OCF_RESKEY_CRM_meta_notify_start_resource``
-* Resources that were promoted: ``$OCF_RESKEY_CRM_meta_notify_promote_resource``
-* Resources that were demoted: ``$OCF_RESKEY_CRM_meta_notify_demote_resource``
-* Resources that were stopped: ``$OCF_RESKEY_CRM_meta_notify_stop_resource``
-
Monitoring Promotable Clone Resources
_____________________________________
diff --git a/doc/sphinx/Pacemaker_Explained/constraints.rst b/doc/sphinx/Pacemaker_Explained/constraints.rst
index a78d6c2..cff65c5 100644
--- a/doc/sphinx/Pacemaker_Explained/constraints.rst
+++ b/doc/sphinx/Pacemaker_Explained/constraints.rst
@@ -38,109 +38,146 @@ configuration might be simpler.
Location Properties
___________________
-.. table:: **Attributes of a rsc_location Element**
+.. list-table:: **Attributes of a rsc_location Element**
:class: longtable
- :widths: 1 1 4
-
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | Attribute | Default | Description |
- +====================+=========+==============================================================================================+
- | id | | .. index:: |
- | | | single: rsc_location; attribute, id |
- | | | single: attribute; id (rsc_location) |
- | | | single: id; rsc_location attribute |
- | | | |
- | | | A unique name for the constraint (required) |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | rsc | | .. index:: |
- | | | single: rsc_location; attribute, rsc |
- | | | single: attribute; rsc (rsc_location) |
- | | | single: rsc; rsc_location attribute |
- | | | |
- | | | The name of the resource to which this constraint |
- | | | applies. A location constraint must either have a |
- | | | ``rsc``, have a ``rsc-pattern``, or contain at |
- | | | least one resource set. |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | rsc-pattern | | .. index:: |
- | | | single: rsc_location; attribute, rsc-pattern |
- | | | single: attribute; rsc-pattern (rsc_location) |
- | | | single: rsc-pattern; rsc_location attribute |
- | | | |
- | | | A pattern matching the names of resources to which |
- | | | this constraint applies. The syntax is the same as |
- | | | `POSIX <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04>`_ |
- | | | extended regular expressions, with the addition of an |
- | | | initial ``!`` indicating that resources *not* matching |
- | | | the pattern are selected. If the regular expression |
- | | | contains submatches, and the constraint is governed by |
- | | | a :ref:`rule <rules>`, the submatches can be |
- | | | referenced as ``%1`` through ``%9`` in the rule's |
- | | | ``score-attribute`` or a rule expression's ``attribute`` |
- | | | (see :ref:`s-rsc-pattern-rules`). A location constraint |
- | | | must either have a ``rsc``, have a ``rsc-pattern``, or |
- | | | contain at least one resource set. |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | node | | .. index:: |
- | | | single: rsc_location; attribute, node |
- | | | single: attribute; node (rsc_location) |
- | | | single: node; rsc_location attribute |
- | | | |
- | | | The name of the node to which this constraint applies. |
- | | | A location constraint must either have a ``node`` and |
- | | | ``score``, or contain at least one rule. |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | score | | .. index:: |
- | | | single: rsc_location; attribute, score |
- | | | single: attribute; score (rsc_location) |
- | | | single: score; rsc_location attribute |
- | | | |
- | | | Positive values indicate a preference for running the |
- | | | affected resource(s) on ``node`` -- the higher the value, |
- | | | the stronger the preference. Negative values indicate |
- | | | the resource(s) should avoid this node (a value of |
- | | | **-INFINITY** changes "should" to "must"). A location |
- | | | constraint must either have a ``node`` and ``score``, |
- | | | or contain at least one rule. |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
- | resource-discovery | always | .. index:: |
- | | | single: rsc_location; attribute, resource-discovery |
- | | | single: attribute; resource-discovery (rsc_location) |
- | | | single: resource-discovery; rsc_location attribute |
- | | | |
- | | | Whether Pacemaker should perform resource discovery |
- | | | (that is, check whether the resource is already running) |
- | | | for this resource on this node. This should normally be |
- | | | left as the default, so that rogue instances of a |
- | | | service can be stopped when they are running where they |
- | | | are not supposed to be. However, there are two |
- | | | situations where disabling resource discovery is a good |
- | | | idea: when a service is not installed on a node, |
- | | | discovery might return an error (properly written OCF |
- | | | agents will not, so this is usually only seen with other |
- | | | agent types); and when Pacemaker Remote is used to scale |
- | | | a cluster to hundreds of nodes, limiting resource |
- | | | discovery to allowed nodes can significantly boost |
- | | | performance. |
- | | | |
- | | | * ``always:`` Always perform resource discovery for |
- | | | the specified resource on this node. |
- | | | |
- | | | * ``never:`` Never perform resource discovery for the |
- | | | specified resource on this node. This option should |
- | | | generally be used with a -INFINITY score, although |
- | | | that is not strictly required. |
- | | | |
- | | | * ``exclusive:`` Perform resource discovery for the |
- | | | specified resource only on this node (and other nodes |
- | | | similarly marked as ``exclusive``). Multiple location |
- | | | constraints using ``exclusive`` discovery for the |
- | | | same resource across different nodes creates a subset |
- | | | of nodes resource-discovery is exclusive to. If a |
- | | | resource is marked for ``exclusive`` discovery on one |
- | | | or more nodes, that resource is only allowed to be |
- | | | placed within that subset of nodes. |
- +--------------------+---------+----------------------------------------------------------------------------------------------+
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. rsc_location_id:
+
+ .. index::
+ single: rsc_location; attribute, id
+ single: attribute; id (rsc_location)
+ single: id; rsc_location attribute
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for the constraint (required)
+ * - .. rsc_location_rsc:
+
+ .. index::
+ single: rsc_location; attribute, rsc
+ single: attribute; rsc (rsc_location)
+ single: rsc; rsc_location attribute
+
+ rsc
+ - :ref:`id <id>`
+ -
+ - The name of the resource to which this constraint applies. A location
+ constraint must either have a ``rsc``, have a ``rsc-pattern``, or
+ contain at least one resource set.
+ * - .. rsc_pattern:
+
+ .. index::
+ single: rsc_location; attribute, rsc-pattern
+ single: attribute; rsc-pattern (rsc_location)
+ single: rsc-pattern; rsc_location attribute
+
+ rsc-pattern
+ - :ref:`text <text>`
+ -
+ - A pattern matching the names of resources to which this constraint
+ applies. The syntax is the same as `POSIX
+ <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04>`_
+ extended regular expressions, with the addition of an initial ``!``
+ indicating that resources *not* matching the pattern are selected. If
+ the regular expression contains submatches, and the constraint contains
+ a :ref:`rule <rules>`, the submatches can be referenced as ``%1``
+ through ``%9`` in the rule's ``score-attribute`` or a rule expression's
+ ``attribute`` (see :ref:`s-rsc-pattern-rules`). A location constraint
+ must either have a ``rsc``, have a ``rsc-pattern``, or contain at least
+ one resource set.
+ * - .. rsc_location_node:
+
+ .. index::
+ single: rsc_location; attribute, node
+ single: attribute; node (rsc_location)
+ single: node; rsc_location attribute
+
+ node
+ - :ref:`text <text>`
+ -
+ - The name of the node to which this constraint applies. A location
+ constraint must either have a ``node`` and ``score``, or contain at
+ least one rule.
+ * - .. rsc_location_score:
+
+ .. index::
+ single: rsc_location; attribute, score
+ single: attribute; score (rsc_location)
+ single: score; rsc_location attribute
+
+ score
+ - :ref:`score <score>`
+ -
+ - Positive values indicate a preference for running the affected
+ resource(s) on ``node`` -- the higher the value, the stronger the
+ preference. Negative values indicate the resource(s) should avoid this
+ node (a value of **-INFINITY** changes "should" to "must"). A location
+ constraint must either have a ``node`` and ``score``, or contain at
+ least one rule.
+ * - .. rsc_location_role:
+
+ .. index::
+ single: rsc_location; attribute, role
+ single: attribute; role (rsc_location)
+ single: role; rsc_location attribute
+
+ role
+ - :ref:`enumeration <enumeration>`
+ - ``Started``
+ - This is significant only for
+ :ref:`promotable clones <s-resource-promotable>`, is allowed only if
+ ``rsc`` or ``rsc-pattern`` is set, and is ignored if the constraint
+ contains a rule. Allowed values:
+
+ * ``Started`` or ``Unpromoted``: The constraint affects the location of
+ all instances of the resource. (A promoted instance must start in the
+ unpromoted role before being promoted, so any location requirement for
+ unpromoted instances also affects promoted instances.)
+ * ``Promoted``: The constraint does not affect the location of
+ instances, but instead affects which of the instances will be
+ promoted.
+
+ * - .. resource_discovery:
+
+ .. index::
+ single: rsc_location; attribute, resource-discovery
+ single: attribute; resource-discovery (rsc_location)
+ single: resource-discovery; rsc_location attribute
+
+ resource-discovery
+ - :ref:`enumeration <enumeration>`
+ - always
+ - Whether Pacemaker should perform resource discovery (that is, check
+ whether the resource is already running) for this resource on this node.
+ This should normally be left as the default, so that rogue instances of
+ a service can be stopped when they are running where they are not
+ supposed to be. However, there are two situations where disabling
+ resource discovery is a good idea: when a service is not installed on a
+ node, discovery might return an error (properly written OCF agents will
+ not, so this is usually only seen with other agent types); and when
+ Pacemaker Remote is used to scale a cluster to hundreds of nodes,
+ limiting resource discovery to allowed nodes can significantly boost
+ performance. Allowed values:
+
+ * ``always:`` Always perform resource discovery for the specified
+ resource on this node.
+ * ``never:`` Never perform resource discovery for the specified resource
+ on this node. This option should generally be used with a -INFINITY
+ score, although that is not strictly required.
+ * ``exclusive:`` Perform resource discovery for the specified resource
+ only on this node (and other nodes similarly marked as ``exclusive``).
+ Multiple location constraints using ``exclusive`` discovery for the
+ same resource across different nodes creates a subset of nodes
+ resource-discovery is exclusive to. If a resource is marked for
+ ``exclusive`` discovery on one or more nodes, that resource is only
+ allowed to be placed within that subset of nodes.
.. warning::
diff --git a/doc/sphinx/Pacemaker_Explained/fencing.rst b/doc/sphinx/Pacemaker_Explained/fencing.rst
index 109b4da..302699f 100644
--- a/doc/sphinx/Pacemaker_Explained/fencing.rst
+++ b/doc/sphinx/Pacemaker_Explained/fencing.rst
@@ -147,8 +147,6 @@ These limitations could be revisited if there is sufficient user demand.
.. index::
single: fencing; special instance attributes
-.. _fencing-attributes:
-
Special Meta-Attributes for Fencing Resources
#############################################
@@ -171,6 +169,8 @@ fencing resource.
| | | | :ref:`unfencing <unfencing>`. |
+----------------------+---------+--------------------+----------------------------------------+
+.. _fencing-attributes:
+
Special Instance Attributes for Fencing Resources
#################################################
@@ -190,319 +190,316 @@ for ``pacemaker-fenced``.
| | | | priority to lowest. |
+----------------------+---------+--------------------+----------------------------------------+
-.. table:: **Additional Properties of Fencing Resources**
+.. list-table:: **Additional Properties of Fencing Resources**
:class: longtable
:widths: 2 1 2 4
-
- +----------------------+---------+--------------------+----------------------------------------+
- | Field | Type | Default | Description |
- +======================+=========+====================+========================================+
- | stonith-timeout | time | | .. index:: |
- | | | | single: stonith-timeout |
- | | | | |
- | | | | This is not used by Pacemaker (see the |
- | | | | ``pcmk_reboot_timeout``, |
- | | | | ``pcmk_off_timeout``, etc. properties |
- | | | | instead), but it may be used by |
- | | | | Linux-HA fence agents. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_host_map | string | | .. index:: |
- | | | | single: pcmk_host_map |
- | | | | |
- | | | | A mapping of node names to ports |
- | | | | for devices that do not understand |
- | | | | the node names. |
- | | | | |
- | | | | Example: ``node1:1;node2:2,3`` tells |
- | | | | the cluster to use port 1 for |
- | | | | ``node1`` and ports 2 and 3 for |
- | | | | ``node2``. If ``pcmk_host_check`` is |
- | | | | explicitly set to ``static-list``, |
- | | | | either this or ``pcmk_host_list`` must |
- | | | | be set. The port portion of the map |
- | | | | may contain special characters such as |
- | | | | spaces if preceded by a backslash |
- | | | | *(since 2.1.2)*. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_host_list | string | | .. index:: |
- | | | | single: pcmk_host_list |
- | | | | |
- | | | | A list of machines controlled by this |
- | | | | device. If ``pcmk_host_check`` is |
- | | | | explicitly set to ``static-list``, |
- | | | | either this or ``pcmk_host_map`` must |
- | | | | be set. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_host_check | string | Value appropriate | .. index:: |
- | | | to other | single: pcmk_host_check |
- | | | parameters (see | |
- | | | "Default Check | The method Pacemaker should use to |
- | | | Type" below) | determine which nodes can be targeted |
- | | | | by this device. Allowed values: |
- | | | | |
- | | | | * ``static-list:`` targets are listed |
- | | | | in the ``pcmk_host_list`` or |
- | | | | ``pcmk_host_map`` attribute |
- | | | | * ``dynamic-list:`` query the device |
- | | | | via the agent's ``list`` action |
- | | | | * ``status:`` query the device via the |
- | | | | agent's ``status`` action |
- | | | | * ``none:`` assume the device can |
- | | | | fence any node |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_delay_max | time | 0s | .. index:: |
- | | | | single: pcmk_delay_max |
- | | | | |
- | | | | 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. This is sometimes |
- | | | | used in two-node clusters to ensure |
- | | | | that the nodes don't fence each other |
- | | | | at the same time. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_delay_base | time | 0s | .. index:: |
- | | | | single: pcmk_delay_base |
- | | | | |
- | | | | Enable a static delay before executing |
- | | | | fencing actions. This can be used, for |
- | | | | example, in two-node clusters to |
- | | | | ensure that the nodes don't fence each |
- | | | | other, by having separate fencing |
- | | | | resources with different values. The |
- | | | | node that is fenced with the shorter |
- | | | | delay will lose a fencing race. The |
- | | | | overall delay introduced by pacemaker |
- | | | | is derived from this value plus a |
- | | | | random delay such that the sum is kept |
- | | | | below the maximum delay. A single |
- | | | | device can have different delays per |
- | | | | node using a host map *(since 2.1.2)*, |
- | | | | for example ``node1:0s;node2:5s.`` |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_action_limit | integer | 1 | .. index:: |
- | | | | single: pcmk_action_limit |
- | | | | |
- | | | | The maximum number of actions that can |
- | | | | be performed in parallel on this |
- | | | | device. A value of -1 means unlimited. |
- | | | | Node fencing actions initiated by the |
- | | | | cluster (as opposed to an administrator|
- | | | | running the ``stonith_admin`` tool or |
- | | | | the fencer running recurring device |
- | | | | monitors and ``status`` and ``list`` |
- | | | | commands) are additionally subject to |
- | | | | the ``concurrent-fencing`` cluster |
- | | | | property. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_host_argument | string | ``port`` otherwise | .. index:: |
- | | | ``plug`` if | single: pcmk_host_argument |
- | | | supported | |
- | | | according to the | *Advanced use only.* Which parameter |
- | | | metadata of the | should be supplied to the fence agent |
- | | | fence agent | to identify the node to be fenced. |
- | | | | Some devices support neither the |
- | | | | standard ``plug`` nor the deprecated |
- | | | | ``port`` parameter, or may provide |
- | | | | additional ones. Use this to specify |
- | | | | an alternate, device-specific |
- | | | | parameter. A value of ``none`` tells |
- | | | | the cluster not to supply any |
- | | | | additional parameters. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_reboot_action | string | reboot | .. index:: |
- | | | | single: pcmk_reboot_action |
- | | | | |
- | | | | *Advanced use only.* The command to |
- | | | | send to the resource agent in order to |
- | | | | reboot a node. Some devices do not |
- | | | | support the standard commands or may |
- | | | | provide additional ones. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | command. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_reboot_timeout | time | 60s | .. index:: |
- | | | | single: pcmk_reboot_timeout |
- | | | | |
- | | | | *Advanced use only.* Specify an |
- | | | | alternate timeout to use for |
- | | | | ``reboot`` actions instead of the |
- | | | | value of ``stonith-timeout``. Some |
- | | | | devices need much more or less time to |
- | | | | complete than normal. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | timeout. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_reboot_retries | integer | 2 | .. index:: |
- | | | | single: pcmk_reboot_retries |
- | | | | |
- | | | | *Advanced use only.* The maximum |
- | | | | number of times to retry the |
- | | | | ``reboot`` command within the timeout |
- | | | | period. Some devices do not support |
- | | | | multiple connections, and operations |
- | | | | may fail if the device is busy with |
- | | | | another task, so Pacemaker will |
- | | | | automatically retry the operation, if |
- | | | | there is time remaining. Use this |
- | | | | option to alter the number of times |
- | | | | Pacemaker retries before giving up. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_off_action | string | off | .. index:: |
- | | | | single: pcmk_off_action |
- | | | | |
- | | | | *Advanced use only.* The command to |
- | | | | send to the resource agent in order to |
- | | | | shut down a node. Some devices do not |
- | | | | support the standard commands or may |
- | | | | provide additional ones. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | command. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_off_timeout | time | 60s | .. index:: |
- | | | | single: pcmk_off_timeout |
- | | | | |
- | | | | *Advanced use only.* Specify an |
- | | | | alternate timeout to use for |
- | | | | ``off`` actions instead of the |
- | | | | value of ``stonith-timeout``. Some |
- | | | | devices need much more or less time to |
- | | | | complete than normal. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | timeout. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_off_retries | integer | 2 | .. index:: |
- | | | | single: pcmk_off_retries |
- | | | | |
- | | | | *Advanced use only.* The maximum |
- | | | | number of times to retry the |
- | | | | ``off`` command within the timeout |
- | | | | period. Some devices do not support |
- | | | | multiple connections, and operations |
- | | | | may fail if the device is busy with |
- | | | | another task, so Pacemaker will |
- | | | | automatically retry the operation, if |
- | | | | there is time remaining. Use this |
- | | | | option to alter the number of times |
- | | | | Pacemaker retries before giving up. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_list_action | string | list | .. index:: |
- | | | | single: pcmk_list_action |
- | | | | |
- | | | | *Advanced use only.* The command to |
- | | | | send to the resource agent in order to |
- | | | | list nodes. Some devices do not |
- | | | | support the standard commands or may |
- | | | | provide additional ones. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | command. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_list_timeout | time | 60s | .. index:: |
- | | | | single: pcmk_list_timeout |
- | | | | |
- | | | | *Advanced use only.* Specify an |
- | | | | alternate timeout to use for |
- | | | | ``list`` actions instead of the |
- | | | | value of ``stonith-timeout``. Some |
- | | | | devices need much more or less time to |
- | | | | complete than normal. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | timeout. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_list_retries | integer | 2 | .. index:: |
- | | | | single: pcmk_list_retries |
- | | | | |
- | | | | *Advanced use only.* The maximum |
- | | | | number of times to retry the |
- | | | | ``list`` command within the timeout |
- | | | | period. Some devices do not support |
- | | | | multiple connections, and operations |
- | | | | may fail if the device is busy with |
- | | | | another task, so Pacemaker will |
- | | | | automatically retry the operation, if |
- | | | | there is time remaining. Use this |
- | | | | option to alter the number of times |
- | | | | Pacemaker retries before giving up. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_monitor_action | string | monitor | .. index:: |
- | | | | single: pcmk_monitor_action |
- | | | | |
- | | | | *Advanced use only.* The command to |
- | | | | send to the resource agent in order to |
- | | | | report extended status. Some devices do|
- | | | | not support the standard commands or |
- | | | | may provide additional ones. Use this |
- | | | | to specify an alternate, |
- | | | | device-specific command. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_monitor_timeout | time | 60s | .. index:: |
- | | | | single: pcmk_monitor_timeout |
- | | | | |
- | | | | *Advanced use only.* Specify an |
- | | | | alternate timeout to use for |
- | | | | ``monitor`` actions instead of the |
- | | | | value of ``stonith-timeout``. Some |
- | | | | devices need much more or less time to |
- | | | | complete than normal. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | timeout. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_monitor_retries | integer | 2 | .. index:: |
- | | | | single: pcmk_monitor_retries |
- | | | | |
- | | | | *Advanced use only.* The maximum |
- | | | | number of times to retry the |
- | | | | ``monitor`` command within the timeout |
- | | | | period. Some devices do not support |
- | | | | multiple connections, and operations |
- | | | | may fail if the device is busy with |
- | | | | another task, so Pacemaker will |
- | | | | automatically retry the operation, if |
- | | | | there is time remaining. Use this |
- | | | | option to alter the number of times |
- | | | | Pacemaker retries before giving up. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_status_action | string | status | .. index:: |
- | | | | single: pcmk_status_action |
- | | | | |
- | | | | *Advanced use only.* The command to |
- | | | | send to the resource agent in order to |
- | | | | report status. Some devices do |
- | | | | not support the standard commands or |
- | | | | may provide additional ones. Use this |
- | | | | to specify an alternate, |
- | | | | device-specific command. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_status_timeout | time | 60s | .. index:: |
- | | | | single: pcmk_status_timeout |
- | | | | |
- | | | | *Advanced use only.* Specify an |
- | | | | alternate timeout to use for |
- | | | | ``status`` actions instead of the |
- | | | | value of ``stonith-timeout``. Some |
- | | | | devices need much more or less time to |
- | | | | complete than normal. Use this to |
- | | | | specify an alternate, device-specific |
- | | | | timeout. |
- +----------------------+---------+--------------------+----------------------------------------+
- | pcmk_status_retries | integer | 2 | .. index:: |
- | | | | single: pcmk_status_retries |
- | | | | |
- | | | | *Advanced use only.* The maximum |
- | | | | number of times to retry the |
- | | | | ``status`` command within the timeout |
- | | | | period. Some devices do not support |
- | | | | multiple connections, and operations |
- | | | | may fail if the device is busy with |
- | | | | another task, so Pacemaker will |
- | | | | automatically retry the operation, if |
- | | | | there is time remaining. Use this |
- | | | | option to alter the number of times |
- | | | | Pacemaker retries before giving up. |
- +----------------------+---------+--------------------+----------------------------------------+
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _primitive_stonith_timeout:
+
+ .. index::
+ single: stonith-timeout (primitive instance attribute)
+
+ stonith-timeout
+ - :ref:`timeout <timeout>`
+ -
+ - This is not used by Pacemaker (see the ``pcmk_reboot_timeout``,
+ ``pcmk_off_timeout``, etc., properties instead), but it may be used by
+ Linux-HA fence agents.
+ * - .. _pcmk_host_map:
+
+ .. index::
+ single: pcmk_host_map
+
+ pcmk_host_map
+ - :ref:`text <text>`
+ -
+ - A mapping of node names to ports for devices that do not understand the
+ node names. For example, ``node1:1;node2:2,3`` tells the cluster to use
+ port 1 for ``node1`` and ports 2 and 3 for ``node2``. If
+ ``pcmk_host_check`` is explicitly set to ``static-list``, either this or
+ ``pcmk_host_list`` must be set. The port portion of the map may contain
+ special characters such as spaces if preceded by a backslash *(since 2.1.2)*.
+ * - .. _pcmk_host_list:
+
+ .. index::
+ single: pcmk_host_list
+
+ pcmk_host_list
+ - :ref:`text <text>`
+ -
+ - 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_host_check:
+
+ .. index::
+ single: pcmk_host_check
+
+ pcmk_host_check
+ - :ref:`text <text>`
+ - See :ref:`pcmk_host_check_default`
+ - The method Pacemaker should use to determine which nodes can be targeted
+ by this device. Allowed values:
+
+ * ``static-list:`` targets are listed in the ``pcmk_host_list`` or ``pcmk_host_map`` attribute
+ * ``dynamic-list:`` query the device via the agent's ``list`` action
+ * ``status:`` query the device via the agent's ``status`` action
+ * ``none:`` assume the device can fence any node
+ * - .. _pcmk_delay_max:
+
+ .. index::
+ single: pcmk_delay_max
+
+ pcmk_delay_max
+ - :ref:`duration <duration>`
+ - 0s
+ - 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. This is sometimes used in two-node clusters to
+ ensure that the nodes don't fence each other at the same time.
+ * - .. _pcmk_delay_base:
+
+ .. index::
+ single: pcmk_delay_base
+
+ pcmk_delay_base
+ - :ref:`text <text>`
+ - 0s
+ - Enable a static delay before executing fencing actions. This can be
+ used, for example, in two-node clusters to ensure that the nodes don't
+ fence each other, by having separate fencing resources with different
+ values. The node that is fenced with the shorter delay will lose a
+ fencing race. The overall delay introduced by pacemaker is derived from
+ this value plus a random delay such that the sum is kept below the
+ maximum delay. A single device can have different delays per node using
+ a host map *(since 2.1.2)*, for example ``node1:0s;node2:5s.``
+ * - .. _pcmk_action_limit:
+
+ .. index::
+ single: pcmk_action_limit
+
+ pcmk_action_limit
+ - :ref:`integer <integer>`
+ - 1
+ - The maximum number of actions that can be performed in parallel on this
+ device. A value of -1 means unlimited. Node fencing actions initiated by
+ the cluster (as opposed to an administrator running the
+ ``stonith_admin`` tool or the fencer running recurring device monitors
+ and ``status`` and ``list`` commands) are additionally subject to the
+ ``concurrent-fencing`` cluster property.
+ * - .. _pcmk_host_argument:
+
+ .. index::
+ single: pcmk_host_argument
+
+ pcmk_host_argument
+ - :ref:`text <text>`
+ - ``port`` otherwise ``plug`` if supported according to the metadata of
+ the fence agent
+ - *Advanced use only.* Which parameter should be supplied to the fence
+ agent to identify the node to be fenced. Some devices support neither
+ the standard ``plug`` nor the deprecated ``port`` parameter, or may
+ provide additional ones. Use this to specify an alternate,
+ device-specific parameter. A value of ``none`` tells the cluster not to
+ supply any additional parameters.
+ * - .. _pcmk_reboot_action:
+
+ .. index::
+ single: pcmk_reboot_action
+
+ pcmk_reboot_action
+ - :ref:`text <text>`
+ - ``reboot``
+ - *Advanced use only.* The command to send to the resource agent in order
+ to reboot a node. Some devices do not support the standard commands or
+ may provide additional ones. Use this to specify an alternate,
+ device-specific command.
+ * - .. _pcmk_reboot_timeout:
+
+ .. index::
+ single: pcmk_reboot_timeout
+
+ pcmk_reboot_timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - *Advanced use only.* Specify an alternate timeout (in seconds) to use
+ for ``reboot`` actions instead of the value of ``stonith-timeout``. Some
+ devices need much more or less time to complete than normal. Use this to
+ specify an alternate, device-specific timeout.
+ * - .. _pcmk_reboot_retries:
+
+ .. index::
+ single: pcmk_reboot_retries
+
+ pcmk_reboot_retries
+ - :ref:`integer <integer>`
+ - 2
+ - *Advanced use only.* The maximum number of times to retry the ``reboot``
+ command within the timeout period. Some devices do not support multiple
+ connections, and operations may fail if the device is busy with another
+ task, so Pacemaker will automatically retry the operation, if there is
+ time remaining. Use this option to alter the number of times Pacemaker
+ retries before giving up.
+ * - .. _pcmk_off_action:
+
+ .. index::
+ single: pcmk_off_action
+
+ pcmk_off_action
+ - :ref:`text <text>`
+ - ``off``
+ - *Advanced use only.* The command to send to the resource agent in order
+ to shut down a node. Some devices do not support the standard commands or
+ may provide additional ones. Use this to specify an alternate,
+ device-specific command.
+ * - .. _pcmk_off_timeout:
+
+ .. index::
+ single: pcmk_off_timeout
+
+ pcmk_off_timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - *Advanced use only.* Specify an alternate timeout (in seconds) to use
+ for ``off`` actions instead of the value of ``stonith-timeout``. Some
+ devices need much more or less time to complete than normal. Use this to
+ specify an alternate, device-specific timeout.
+ * - .. _pcmk_off_retries:
+
+ .. index::
+ single: pcmk_off_retries
+
+ pcmk_off_retries
+ - :ref:`integer <integer>`
+ - 2
+ - *Advanced use only.* The maximum number of times to retry the ``off``
+ command within the timeout period. Some devices do not support multiple
+ connections, and operations may fail if the device is busy with another
+ task, so Pacemaker will automatically retry the operation, if there is
+ time remaining. Use this option to alter the number of times Pacemaker
+ retries before giving up.
+ * - .. _pcmk_list_action:
+
+ .. index::
+ single: pcmk_list_action
+
+ pcmk_list_action
+ - :ref:`text <text>`
+ - ``list``
+ - *Advanced use only.* The command to send to the resource agent in order
+ to list nodes. Some devices do not support the standard commands or may
+ provide additional ones. Use this to specify an alternate,
+ device-specific command.
+ * - .. _pcmk_list_timeout:
+
+ .. index::
+ single: pcmk_list_timeout
+
+ pcmk_list_timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - *Advanced use only.* Specify an alternate timeout (in seconds) to use
+ for ``list`` actions instead of the value of ``stonith-timeout``. Some
+ devices need much more or less time to complete than normal. Use this to
+ specify an alternate, device-specific timeout.
+ * - .. _pcmk_list_retries:
+
+ .. index::
+ single: pcmk_list_retries
+
+ pcmk_list_retries
+ - :ref:`integer <integer>`
+ - 2
+ - *Advanced use only.* The maximum number of times to retry the ``list``
+ command within the timeout period. Some devices do not support multiple
+ connections, and operations may fail if the device is busy with another
+ task, so Pacemaker will automatically retry the operation, if there is
+ time remaining. Use this option to alter the number of times Pacemaker
+ retries before giving up.
+ * - .. _pcmk_monitor_action:
+
+ .. index::
+ single: pcmk_monitor_action
+
+ pcmk_monitor_action
+ - :ref:`text <text>`
+ - ``monitor``
+ - *Advanced use only.* The command to send to the resource agent in order
+ to report extended status. Some devices do not support the standard
+ commands or may provide additional ones. Use this to specify an
+ alternate, device-specific command.
+ * - .. _pcmk_monitor_timeout:
+
+ .. index::
+ single: pcmk_monitor_timeout
+
+ pcmk_monitor_timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - *Advanced use only.* Specify an alternate timeout (in seconds) to use
+ for ``monitor`` actions instead of the value of ``stonith-timeout``. Some
+ devices need much more or less time to complete than normal. Use this to
+ specify an alternate, device-specific timeout.
+ * - .. _pcmk_monitor_retries:
+
+ .. index::
+ single: pcmk_monitor_retries
+
+ pcmk_monitor_retries
+ - :ref:`integer <integer>`
+ - 2
+ - *Advanced use only.* The maximum number of times to retry the ``monitor``
+ command within the timeout period. Some devices do not support multiple
+ connections, and operations may fail if the device is busy with another
+ task, so Pacemaker will automatically retry the operation, if there is
+ time remaining. Use this option to alter the number of times Pacemaker
+ retries before giving up.
+ * - .. _pcmk_status_action:
+
+ .. index::
+ single: pcmk_status_action
+
+ pcmk_status_action
+ - :ref:`text <text>`
+ - ``status``
+ - *Advanced use only.* The command to send to the resource agent in order
+ to report status. Some devices do not support the standard commands or
+ may provide additional ones. Use this to specify an alternate,
+ device-specific command.
+ * - .. _pcmk_status_timeout:
+
+ .. index::
+ single: pcmk_status_timeout
+
+ pcmk_status_timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - *Advanced use only.* Specify an alternate timeout (in seconds) to use
+ for ``status`` actions instead of the value of ``stonith-timeout``. Some
+ devices need much more or less time to complete than normal. Use this to
+ specify an alternate, device-specific timeout.
+ * - .. _pcmk_status_retries:
+
+ .. index::
+ single: pcmk_status_retries
+
+ pcmk_status_retries
+ - :ref:`integer <integer>`
+ - 2
+ - *Advanced use only.* The maximum number of times to retry the ``status``
+ command within the timeout period. Some devices do not support multiple
+ connections, and operations may fail if the device is busy with another
+ task, so Pacemaker will automatically retry the operation, if there is
+ time remaining. Use this option to alter the number of times Pacemaker
+ retries before giving up.
+
+.. _pcmk_host_check_default:
Default Check Type
##################
diff --git a/doc/sphinx/Pacemaker_Explained/local-options.rst b/doc/sphinx/Pacemaker_Explained/local-options.rst
index 91eda66..915a65b 100644
--- a/doc/sphinx/Pacemaker_Explained/local-options.rst
+++ b/doc/sphinx/Pacemaker_Explained/local-options.rst
@@ -8,6 +8,188 @@ Host-Local Configuration
your Pacemaker build settings. Check your Pacemaker configuration
file to find the correct paths.
+Configuration Value Types
+#########################
+
+Throughout this document, configuration values will be designated as having one
+of the following types:
+
+.. list-table:: **Configuration Value Types**
+ :class: longtable
+ :widths: 1 3
+ :header-rows: 1
+
+ * - Type
+ - Description
+ * - .. _boolean:
+
+ .. index::
+ pair: type; boolean
+
+ boolean
+ - Case-insensitive text value where ``1``, ``yes``, ``y``, ``on``,
+ and ``true`` evaluate as true and ``0``, ``no``, ``n``, ``off``,
+ ``false``, and unset evaluate as false
+ * - .. _date_time:
+
+ .. index::
+ pair: type; date/time
+
+ date/time
+ - Textual timestamp like ``Sat Dec 21 11:47:45 2013``
+ * - .. _duration:
+
+ .. index::
+ pair: type; duration
+
+ duration
+ - A time duration, specified either like a :ref:`timeout <timeout>` or an
+ `ISO 8601 duration <https://en.wikipedia.org/wiki/ISO_8601#Durations>`_.
+ A duration may be up to approximately 49 days but is intended for much
+ smaller time periods.
+ * - .. _enumeration:
+
+ .. index::
+ pair: type; enumeration
+
+ enumeration
+ - Text that must be one of a set of defined values (which will be listed
+ in the description)
+ * - .. _epoch_time:
+
+ .. index::
+ pair: type; epoch_time
+
+ epoch_time
+ - Time as the integer number of seconds since the Unix epoch,
+ ``1970-01-01 00:00:00 +0000 (UTC)``.
+ * - .. _id:
+
+ .. index::
+ pair: type; id
+
+ id
+ - A text string starting with a letter or underbar, followed by any
+ combination of letters, numbers, dashes, dots, and/or underbars; when
+ used for a property named ``id``, the string must be unique across all
+ ``id`` properties in the CIB
+ * - .. _integer:
+
+ .. index::
+ pair: type; integer
+
+ integer
+ - 32-bit signed integer value (-2,147,483,648 to 2,147,483,647)
+ * - .. _iso8601:
+
+ .. index::
+ pair: type; iso8601
+
+ ISO 8601
+ - An `ISO 8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ date/time.
+ * - .. _nonnegative_integer:
+
+ .. index::
+ pair: type; nonnegative integer
+
+ nonnegative integer
+ - 32-bit nonnegative integer value (0 to 2,147,483,647)
+ * - .. _percentage:
+
+ .. index::
+ pair: type; percentage
+
+ percentage
+ - Floating-point number followed by an optional percent sign ('%')
+ * - .. _port:
+
+ .. index::
+ pair: type; port
+
+ port
+ - Integer TCP port number (0 to 65535)
+ * - .. _range:
+
+ .. index::
+ pair: type; range
+
+ range
+ - A range may be a single nonnegative integer or a dash-separated range of
+ nonnegative integers. Either the first or last value may be omitted to
+ leave the range open-ended. Examples: ``0``, ``3-``, ``-5``, ``4-6``.
+ * - .. _score:
+
+ .. index::
+ pair: type; score
+
+ score
+ - A Pacemaker score can be an integer between -1,000,000 and 1,000,000, or
+ a string alias: ``INFINITY`` or ``+INFINITY`` is equivalent to
+ 1,000,000, ``-INFINITY`` is equivalent to -1,000,000, and ``red``,
+ ``yellow``, and ``green`` are equivalent to integers as described in
+ :ref:`node-health`.
+ * - .. _text:
+
+ .. index::
+ pair: type; text
+
+ text
+ - A text string
+ * - .. _timeout:
+
+ .. index::
+ pair: type; timeout
+
+ timeout
+ - A time duration, specified as a bare number (in which case it is
+ considered to be in seconds) or a number with a unit (``ms`` or ``msec``
+ for milliseconds, ``us`` or ``usec`` for microseconds, ``s`` or ``sec``
+ for seconds, ``m`` or ``min`` for minutes, ``h`` or ``hr`` for hours)
+ optionally with whitespace before and/or after the number.
+ * - .. _version:
+
+ .. index::
+ pair: type; version
+
+ version
+ - Version number (any combination of alphanumeric characters, dots, and
+ dashes, starting with a number).
+
+
+Scores
+______
+
+Scores are integral to how Pacemaker works. Practically everything from moving
+a resource to deciding which resource to stop in a degraded cluster is achieved
+by manipulating scores in some way.
+
+Scores are calculated per resource and node. Any node with a negative score for
+a resource can't run that resource. The cluster places a resource on the node
+with the highest score for it.
+
+Score addition and subtraction follow these rules:
+
+* Any value (including ``INFINITY``) - ``INFINITY`` = ``-INFINITY``
+* ``INFINITY`` + any value other than ``-INFINITY`` = ``INFINITY``
+
+.. note::
+
+ What if you want to use a score higher than 1,000,000? Typically this possibility
+ arises when someone wants to base the score on some external metric that might
+ go above 1,000,000.
+
+ The short answer is you can't.
+
+ The long answer is it is sometimes possible work around this limitation
+ creatively. You may be able to set the score to some computed value based on
+ the external metric rather than use the metric directly. For nodes, you can
+ store the metric as a node attribute, and query the attribute when computing
+ the score (possibly as part of a custom resource agent).
+
+
+Local Options
+#############
+
Pacemaker supports several host-local configuration options. These options can
be configured on each node in the main Pacemaker configuration file
(|PCMK_CONFIG_FILE|) in the format ``<NAME>="<VALUE>"``. They work by setting
@@ -22,6 +204,18 @@ environment variables when Pacemaker daemons start up.
- Type
- Default
- Description
+
+ * - .. _cib_pam_service:
+
+ .. index::
+ pair: node option; CIB_pam_service
+
+ CIB_pam_service
+ - :ref:`text <text>`
+ - login
+ - PAM service to use for remote CIB client authentication (passed to
+ ``pam_start``).
+
* - .. _pcmk_logfacility:
.. index::
@@ -50,7 +244,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_logpriority:
.. index::
- pair:: node option; PCMK_logpriority
+ pair: node option; PCMK_logpriority
PCMK_logpriority
- :ref:`enumeration <enumeration>`
@@ -72,7 +266,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_logfile:
.. index::
- pair:: node option; PCMK_logfile
+ pair: node option; PCMK_logfile
PCMK_logfile
- :ref:`text <text>`
@@ -81,12 +275,15 @@ environment variables when Pacemaker daemons start up.
specified file (in addition to the system log, if enabled). These
messages may have extended information, and will include messages of info
severity. This log is of more use to developers and advanced system
- administrators, and when reporting problems.
+ administrators, and when reporting problems. Note: The default is
+ |PCMK_CONTAINER_LOG_FILE| (inside the container) for bundled container
+ nodes; this would typically be mapped to a different path on the host
+ running the container.
* - .. _pcmk_logfile_mode:
.. index::
- pair:: node option; PCMK_logfile_mode
+ pair: node option; PCMK_logfile_mode
PCMK_logfile_mode
- :ref:`text <text>`
@@ -97,7 +294,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_debug:
.. index::
- pair:: node option; PCMK_debug
+ pair: node option; PCMK_debug
PCMK_debug
- :ref:`enumeration <enumeration>`
@@ -119,7 +316,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_stderr:
.. index::
- pair:: node option; PCMK_stderr
+ pair: node option; PCMK_stderr
PCMK_stderr
- :ref:`boolean <boolean>`
@@ -135,7 +332,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_trace_functions:
.. index::
- pair:: node option; PCMK_trace_functions
+ pair: node option; PCMK_trace_functions
PCMK_trace_functions
- :ref:`text <text>`
@@ -149,7 +346,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_trace_files:
.. index::
- pair:: node option; PCMK_trace_files
+ pair: node option; PCMK_trace_files
PCMK_trace_files
- :ref:`text <text>`
@@ -162,7 +359,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_trace_formats:
.. index::
- pair:: node option; PCMK_trace_formats
+ pair: node option; PCMK_trace_formats
PCMK_trace_formats
- :ref:`text <text>`
@@ -176,7 +373,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_trace_tags:
.. index::
- pair:: node option; PCMK_trace_tags
+ pair: node option; PCMK_trace_tags
PCMK_trace_tags
- :ref:`text <text>`
@@ -189,7 +386,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_blackbox:
.. index::
- pair:: node option; PCMK_blackbox
+ pair: node option; PCMK_blackbox
PCMK_blackbox
- :ref:`enumeration <enumeration>`
@@ -214,7 +411,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_trace_blackbox:
.. index::
- pair:: node option; PCMK_trace_blackbox
+ pair: node option; PCMK_trace_blackbox
PCMK_trace_blackbox
- :ref:`enumeration <enumeration>`
@@ -228,7 +425,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_node_start_state:
.. index::
- pair:: node option; PCMK_node_start_state
+ pair: node option; PCMK_node_start_state
PCMK_node_start_state
- :ref:`enumeration <enumeration>`
@@ -241,19 +438,19 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_node_action_limit:
.. index::
- pair:: node option; PCMK_node_action_limit
+ pair: node option; PCMK_node_action_limit
PCMK_node_action_limit
- :ref:`nonnegative integer <nonnegative_integer>`
-
- Specify the maximum number of jobs that can be scheduled on this node. If
- set, this overrides the ``node-action-limit`` cluster property for this
- node.
+ set, this overrides the :ref:`node-action-limit <node_action_limit>`
+ cluster option on this node.
* - .. _pcmk_shutdown_delay:
.. index::
- pair:: node option; PCMK_shutdown_delay
+ pair: node option; PCMK_shutdown_delay
PCMK_shutdown_delay
- :ref:`timeout <timeout>`
@@ -264,7 +461,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_fail_fast:
.. index::
- pair:: node option; PCMK_fail_fast
+ pair: node option; PCMK_fail_fast
PCMK_fail_fast
- :ref:`boolean <boolean>`
@@ -276,7 +473,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_panic_action:
.. index::
- pair:: node option; PCMK_panic_action
+ pair: node option; PCMK_panic_action
PCMK_panic_action
- :ref:`enumeration <enumeration>`
@@ -292,7 +489,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_authkey_location:
.. index::
- pair:: node option; PCMK_authkey_location
+ pair: node option; PCMK_authkey_location
PCMK_authkey_location
- :ref:`text <text>`
@@ -306,7 +503,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_remote_address:
.. index::
- pair:: node option; PCMK_remote_address
+ pair: node option; PCMK_remote_address
PCMK_remote_address
- :ref:`text <text>`
@@ -323,7 +520,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_remote_port:
.. index::
- pair:: node option; PCMK_remote_port
+ pair: node option; PCMK_remote_port
PCMK_remote_port
- :ref:`port <port>`
@@ -334,7 +531,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_remote_pid1:
.. index::
- pair:: node option; PCMK_remote_pid1
+ pair: node option; PCMK_remote_pid1
PCMK_remote_pid1
- :ref:`enumeration <enumeration>`
@@ -362,7 +559,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_tls_priorities:
.. index::
- pair:: node option; PCMK_tls_priorities
+ pair: node option; PCMK_tls_priorities
PCMK_tls_priorities
- :ref:`text <text>`
@@ -383,7 +580,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_dh_min_bits:
.. index::
- pair:: node option; PCMK_dh_min_bits
+ pair: node option; PCMK_dh_min_bits
PCMK_dh_min_bits
- :ref:`nonnegative integer <nonnegative_integer>`
@@ -407,7 +604,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_dh_max_bits:
.. index::
- pair:: node option; PCMK_dh_max_bits
+ pair: node option; PCMK_dh_max_bits
PCMK_dh_max_bits
- :ref:`nonnegative integer <nonnegative_integer>`
@@ -428,7 +625,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_ipc_type:
.. index::
- pair:: node option; PCMK_ipc_type
+ pair: node option; PCMK_ipc_type
PCMK_ipc_type
- :ref:`enumeration <enumeration>`
@@ -443,7 +640,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_ipc_buffer:
.. index::
- pair:: node option; PCMK_ipc_buffer
+ pair: node option; PCMK_ipc_buffer
PCMK_ipc_buffer
- :ref:`nonnegative integer <nonnegative_integer>`
@@ -456,7 +653,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_cluster_type:
.. index::
- pair:: node option; PCMK_cluster_type
+ pair: node option; PCMK_cluster_type
PCMK_cluster_type
- :ref:`enumeration <enumeration>`
@@ -470,7 +667,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_schema_directory:
.. index::
- pair:: node option; PCMK_schema_directory
+ pair: node option; PCMK_schema_directory
PCMK_schema_directory
- :ref:`text <text>`
@@ -478,10 +675,22 @@ environment variables when Pacemaker daemons start up.
- *Advanced Use Only:* Specify an alternate location for RNG schemas and
XSL transforms.
+ * - .. _pcmk_remote_schema_directory:
+
+ .. index::
+ pair: node option; PCMK_remote_schema_directory
+
+ PCMK_remote_schema_directory
+ - :ref:`text <text>`
+ - |PCMK__REMOTE_SCHEMA_DIR|
+ - *Advanced Use Only:* Specify an alternate location on Pacemaker Remote
+ nodes for storing newer RNG schemas and XSL transforms fetched from
+ the cluster.
+
* - .. _pcmk_valgrind_enabled:
.. index::
- pair:: node option; PCMK_valgrind_enabled
+ pair: node option; PCMK_valgrind_enabled
PCMK_valgrind_enabled
- :ref:`enumeration <enumeration>`
@@ -492,7 +701,7 @@ environment variables when Pacemaker daemons start up.
* - .. _pcmk_callgrind_enabled:
.. index::
- pair:: node option; PCMK_callgrind_enabled
+ pair: node option; PCMK_callgrind_enabled
PCMK_callgrind_enabled
- :ref:`enumeration <enumeration>`
@@ -501,10 +710,36 @@ environment variables when Pacemaker daemons start up.
``valgrind`` with the ``callgrind`` tool enabled. Allowed values are the
same as for ``PCMK_debug``.
+ * - .. _sbd_sync_resource_startup:
+
+ .. index::
+ pair:: node option; SBD_SYNC_RESOURCE_STARTUP
+
+ SBD_SYNC_RESOURCE_STARTUP
+ - :ref:`boolean <boolean>`
+ -
+ - If true, ``pacemakerd`` waits for a ping from ``sbd`` during startup
+ before starting other Pacemaker daemons, and during shutdown after
+ stopping other Pacemaker daemons but before exiting. Default value is set
+ based on the ``--with-sbd-sync-default`` configure script option.
+
+ * - .. _sbd_watchdog_timeout:
+
+ .. index::
+ pair:: node option; SBD_WATCHDOG_TIMEOUT
+
+ SBD_WATCHDOG_TIMEOUT
+ - :ref:`duration <duration>`
+ -
+ - If the ``stonith-watchdog-timeout`` cluster property is set to a negative
+ or invalid value, use double this value as the default if positive, or
+ use 0 as the default otherwise. This value must be greater than the value
+ of ``stonith-watchdog-timeout`` if both are set.
+
* - .. _valgrind_opts:
.. index::
- pair:: node option; VALGRIND_OPTS
+ pair: node option; VALGRIND_OPTS
VALGRIND_OPTS
- :ref:`text <text>`
diff --git a/doc/sphinx/Pacemaker_Explained/nodes.rst b/doc/sphinx/Pacemaker_Explained/nodes.rst
index 378b067..b700010 100644
--- a/doc/sphinx/Pacemaker_Explained/nodes.rst
+++ b/doc/sphinx/Pacemaker_Explained/nodes.rst
@@ -105,6 +105,44 @@ To read back the value that was just set:
The ``--type nodes`` indicates that this is a permanent node attribute;
``--type status`` would indicate a transient node attribute.
+.. warning::
+
+ Attribute values with newline or tab characters are currently displayed with
+ newlines as ``"\n"`` and tabs as ``"\t"``, when ``crm_attribute`` or
+ ``attrd_updater`` query commands use ``--output-as=text`` or leave
+ ``--output-as`` unspecified:
+
+ .. code-block:: none
+
+ # crm_attribute -N node1 -n test_attr -v "$(echo -e "a\nb\tc")" -t status
+ # crm_attribute -N node1 -n test_attr --query -t status
+ scope=status name=test_attr value=a\nb\tc
+
+ This format is deprecated. In a future release, the values will be displayed
+ with literal whitespace characters:
+
+ .. code-block:: none
+
+ # crm_attribute -N node1 -n test_attr --query -t status
+ scope=status name=test_attr value=a
+ b c
+
+ Users should either avoid attribute values with newlines and tabs, or ensure
+ that they can handle both formats.
+
+ However, it's best to use ``--output-as=xml`` when parsing attribute values
+ from output. Newlines, tabs, and special characters are replaced with XML
+ character references that a conforming XML processor can recognize and
+ convert to literals *(since 2.1.8)*:
+
+ .. code-block:: none
+
+ # crm_attribute -N node1 -n test_attr --query -t status --output-as=xml
+ <pacemaker-result api-version="2.35" request="crm_attribute -N laptop -n test_attr --query -t status --output-as=xml">
+ <attribute name="test_attr" value="a&#10;b&#9;c" scope="status"/>
+ <status code="0" message="OK"/>
+ </pacemaker-result>
+
.. _special_node_attributes:
diff --git a/doc/sphinx/Pacemaker_Explained/operations.rst b/doc/sphinx/Pacemaker_Explained/operations.rst
index b1ad65d..c831f81 100644
--- a/doc/sphinx/Pacemaker_Explained/operations.rst
+++ b/doc/sphinx/Pacemaker_Explained/operations.rst
@@ -38,113 +38,160 @@ two operations for the same resource with the same name and interval.
Operation Properties
####################
-Operation properties may be specified directly in the ``op`` element as
-XML attributes, or in a separate ``meta_attributes`` block as ``nvpair`` elements.
-XML attributes take precedence over ``nvpair`` elements if both are specified.
+The ``id``, ``name``, ``interval``, and ``role`` operation properties may be
+specified only as XML attributes of the ``op`` element. Other operation
+properties may be specified in any of the following ways, from highest
+precedence to lowest:
-.. table:: **Properties of an Operation**
+* directly in the ``op`` element as an XML attribute
+* in an ``nvpair`` element within a ``meta_attributes`` element within the
+ ``op`` element
+* in an ``nvpair`` element within a ``meta_attributes`` element within
+ :ref:`operation defaults <s-operation-defaults>`
+
+If not specified, the default from the table below is used.
+
+.. list-table:: **Operation Properties**
:class: longtable
- :widths: 1 2 3
-
- +----------------+-----------------------------------+-----------------------------------------------------+
- | Field | Default | Description |
- +================+===================================+=====================================================+
- | id | | .. index:: |
- | | | single: id; action property |
- | | | single: action; property, id |
- | | | |
- | | | A unique name for the operation. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | name | | .. index:: |
- | | | single: name; action property |
- | | | single: action; property, name |
- | | | |
- | | | The action to perform. This can be any action |
- | | | supported by the agent; common values include |
- | | | ``monitor``, ``start``, and ``stop``. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | interval | 0 | .. index:: |
- | | | single: interval; action property |
- | | | single: action; property, interval |
- | | | |
- | | | How frequently (in seconds) to perform the |
- | | | operation. A value of 0 means "when needed". |
- | | | A positive value defines a *recurring action*, |
- | | | which is typically used with |
- | | | :ref:`monitor <s-resource-monitoring>`. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | timeout | | .. index:: |
- | | | single: timeout; action property |
- | | | single: action; property, timeout |
- | | | |
- | | | How long to wait before declaring the action |
- | | | has failed |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | on-fail | Varies by action: | .. index:: |
- | | | single: on-fail; action property |
- | | * ``stop``: ``fence`` if | single: action; property, on-fail |
- | | ``stonith-enabled`` is true | |
- | | or ``block`` otherwise | The action to take if this action ever fails. |
- | | * ``demote``: ``on-fail`` of the | Allowed values: |
- | | ``monitor`` action with | |
- | | ``role`` set to ``Promoted``, | * ``ignore:`` Pretend the resource did not fail. |
- | | if present, enabled, and | * ``block:`` Don't perform any further operations |
- | | configured to a value other | on the resource. |
- | | than ``demote``, or ``restart`` | * ``stop:`` Stop the resource and do not start |
- | | otherwise | it elsewhere. |
- | | * all other actions: ``restart`` | * ``demote:`` Demote the resource, without a |
- | | | full restart. This is valid only for ``promote`` |
- | | | actions, and for ``monitor`` actions with both |
- | | | a nonzero ``interval`` and ``role`` set to |
- | | | ``Promoted``; for any other action, a |
- | | | configuration error will be logged, and the |
- | | | default behavior will be used. *(since 2.0.5)* |
- | | | * ``restart:`` Stop the resource and start it |
- | | | again (possibly on a different node). |
- | | | * ``fence:`` STONITH the node on which the |
- | | | resource failed. |
- | | | * ``standby:`` Move *all* resources away from the |
- | | | node on which the resource failed. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | enabled | TRUE | .. _op_enabled: |
- | | | |
- | | | .. index:: |
- | | | single: enabled; action property |
- | | | single: action; property, enabled |
- | | | |
- | | | If ``false``, ignore this operation definition. |
- | | | This does not suppress all actions of this type, |
- | | | but is typically used to pause a recurring monitor. |
- | | | This can complement the resource being unmanaged |
- | | | (:ref:`is-managed <is_managed>` set to ``false``), |
- | | | which does not stop recurring operations. |
- | | | Maintenance mode, which does stop configured this |
- | | | monitors, overrides this setting. Allowed values: |
- | | | ``true``, ``false``. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | record-pending | TRUE | .. index:: |
- | | | single: record-pending; action property |
- | | | single: action; property, record-pending |
- | | | |
- | | | If ``true``, the intention to perform the operation |
- | | | is recorded so that GUIs and CLI tools can indicate |
- | | | that an operation is in progress. This is best set |
- | | | as an *operation default* |
- | | | (see :ref:`s-operation-defaults`). Allowed values: |
- | | | ``true``, ``false``. |
- +----------------+-----------------------------------+-----------------------------------------------------+
- | role | | .. index:: |
- | | | single: role; action property |
- | | | single: action; property, role |
- | | | |
- | | | Run the operation only on node(s) that the cluster |
- | | | thinks should be in the specified role. This only |
- | | | makes sense for recurring ``monitor`` operations. |
- | | | Allowed (case-sensitive) values: ``Stopped``, |
- | | | ``Started``, and in the case of :ref:`promotable |
- | | | clone resources <s-resource-promotable>`, |
- | | | ``Unpromoted`` and ``Promoted``. |
- +----------------+-----------------------------------+-----------------------------------------------------+
+ :widths: 2 2 3 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _op_id:
+
+ .. index::
+ pair: op; id
+ single: id; action property
+ single: action; property, id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique identifier for the XML element *(required)*
+ * - .. _op_name:
+
+ .. index::
+ pair: op; name
+ single: name; action property
+ single: action; property, name
+
+ name
+ - :ref:`text <text>`
+ -
+ - An action name supported by the resource agent *(required)*
+ * - .. _op_interval:
+
+ .. index::
+ pair: op; interval
+ single: interval; action property
+ single: action; property, interval
+
+ interval
+ - :ref:`duration <duration>`
+ - 0
+ - If this is a positive value, Pacemaker will schedule recurring instances
+ of this operation at the given interval (which makes sense only with
+ :ref:`name <op_name>` set to :ref:`monitor <s-resource-monitoring>`). If
+ this is 0, Pacemaker will apply other properties configured for this
+ operation to instances that are scheduled as needed during normal
+ cluster operation. *(required)*
+ * - .. _op_role:
+
+ .. index::
+ pair: op; role
+ single: role; action property
+ single: action; property, role
+
+ role
+ - :ref:`enumeration <enumeration>`
+ -
+ - If this is set, the operation configuration applies only on nodes where
+ the cluster expects the resource to be in the specified role. This makes
+ sense only for recurring monitors. Allowed values: ``Started``,
+ ``Stopped``, and in the case of :ref:`promotable clone resources
+ <s-resource-promotable>`, ``Unpromoted`` and ``Promoted``.
+ * - .. _op_timeout:
+
+ .. index::
+ pair: op; timeout
+ single: timeout; action property
+ single: action; property, timeout
+
+ timeout
+ - :ref:`timeout <timeout>`
+ - 20s
+ - If resource agent execution does not complete within this amount of
+ time, the action will be considered failed. **Note:** timeouts for
+ fencing agents are handled specially (see the :ref:`fencing` chapter).
+ * - .. _op_on_fail:
+
+ .. index::
+ pair: op; on-fail
+ single: on-fail; action property
+ single: action; property, on-fail
+
+ on-fail
+ - :ref:`enumeration <enumeration>`
+ - * If ``name`` is ``stop``: ``fence`` if
+ :ref:`stonith-enabled <stonith_enabled>` is true, otherwise ``block``
+ * If ``name`` is ``demote``: ``on-fail`` of the ``monitor`` action with
+ ``role`` set to ``Promoted``, if present, enabled, and configured to a
+ value other than ``demote``, or ``restart`` otherwise
+ * Otherwise: ``restart``
+ - How the cluster should respond to a failure of this action. Allowed
+ values:
+
+ * ``ignore:`` Pretend the resource did not fail
+ * ``block:`` Do not perform any further operations on the resource
+ * ``stop:`` Stop the resource and leave it stopped
+ * ``demote:`` Demote the resource, without a full restart. This is valid
+ only for ``promote`` actions, and for ``monitor`` actions with both a
+ nonzero ``interval`` and ``role`` set to ``Promoted``; for any other
+ action, a configuration error will be logged, and the default behavior
+ will be used. *(since 2.0.5)*
+ * ``restart:`` Stop the resource, and start it again if allowed
+ (possibly on a different node)
+ * ``fence:`` Fence the node on which the resource failed
+ * ``standby:`` Put the node on which the resource failed in standby mode
+ (forcing *all* resources away)
+ * - .. _op_enabled:
+
+ .. index::
+ pair: op; enabled
+ single: enabled; action property
+ single: action; property, enabled
+
+ enabled
+ - :ref:`boolean <boolean>`
+ - true
+ - If ``false``, ignore this operation definition. This does not suppress
+ all actions of this type, but is typically used to pause a recurring
+ monitor. This can complement the resource being unmanaged
+ (:ref:`is-managed <is_managed>` set to ``false``), which does not stop
+ recurring operations. Maintenance mode, which does stop configured
+ monitors, overrides this setting.
+ * - .. _op_record_pending:
+
+ .. index::
+ pair: op; record-pending
+ single: record-pending; action property
+ single: action; property, record-pending
+
+ record-pending
+ - :ref:`boolean <boolean>`
+ - true
+ - Operation results are always recorded when the operation completes
+ (successful or not). If this is ``true``, operations will also be
+ recorded when initiated, so that status output can indicate that the
+ operation is in progress.
+
+.. note::
+
+ Only one action can be configured for any given combination of ``name`` and
+ ``interval``.
.. note::
diff --git a/doc/sphinx/Pacemaker_Explained/resources.rst b/doc/sphinx/Pacemaker_Explained/resources.rst
index a971c44..99bd84f 100644
--- a/doc/sphinx/Pacemaker_Explained/resources.rst
+++ b/doc/sphinx/Pacemaker_Explained/resources.rst
@@ -339,193 +339,291 @@ Meta-attributes are used by the cluster to decide how a resource should
behave and can be easily set using the ``--meta`` option of the
**crm_resource** command.
-.. table:: **Meta-attributes of a Primitive Resource**
+.. list-table:: **Meta-attributes of a Primitive Resource**
:class: longtable
- :widths: 2 2 3
-
- +----------------------------+----------------------------------+------------------------------------------------------+
- | Field | Default | Description |
- +============================+==================================+======================================================+
- | priority | 0 | .. index:: |
- | | | single: priority; resource option |
- | | | single: resource; option, priority |
- | | | |
- | | | If not all resources can be active, the cluster |
- | | | will stop lower priority resources in order to |
- | | | keep higher priority ones active. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | critical | true | .. index:: |
- | | | single: critical; resource option |
- | | | single: resource; option, critical |
- | | | |
- | | | Use this value as the default for ``influence`` in |
- | | | all :ref:`colocation constraints |
- | | | <s-resource-colocation>` involving this resource, |
- | | | as well as the implicit colocation constraints |
- | | | created if this resource is in a |
- | | | :ref:`group <group-resources>`. For details, see |
- | | | :ref:`s-coloc-influence`. *(since 2.1.0)* |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | target-role | Started | .. index:: |
- | | | single: target-role; resource option |
- | | | single: resource; option, target-role |
- | | | |
- | | | What state should the cluster attempt to keep this |
- | | | resource in? Allowed values: |
- | | | |
- | | | * ``Stopped:`` Force the resource to be stopped |
- | | | * ``Started:`` Allow the resource to be started |
- | | | (and in the case of |
- | | | :ref:`promotable <s-resource-promotable>` clone |
- | | | resources, promoted if appropriate) |
- | | | * ``Unpromoted:`` Allow the resource to be started, |
- | | | but only in the unpromoted role if the resource is |
- | | | :ref:`promotable <s-resource-promotable>` |
- | | | * ``Promoted:`` Equivalent to ``Started`` |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | is-managed | TRUE | .. _is_managed: |
- | | | |
- | | | .. index:: |
- | | | single: is-managed; resource option |
- | | | single: resource; option, is-managed |
- | | | |
- | | | If false, the cluster will not start or stop the |
- | | | resource on any node. Recurring actions for the |
- | | | resource are unaffected. Maintenance mode overrides |
- | | | this setting. Allowed values: ``true``, ``false`` |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | maintenance | FALSE | .. _rsc_maintenance: |
- | | | |
- | | | .. index:: |
- | | | single: maintenance; resource option |
- | | | single: resource; option, maintenance |
- | | | |
- | | | If true, the cluster will not start or stop the |
- | | | resource on any node, and will pause any recurring |
- | | | monitors (except those specifying ``role`` as |
- | | | ``Stopped``). If true, the |
- | | | :ref:`maintenance-mode <maintenance_mode>` cluster |
- | | | option or :ref:`maintenance <node_maintenance>` |
- | | | node attribute override this. Allowed values: |
- | | | ``true``, ``false`` |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | resource-stickiness | 1 for individual clone | .. _resource-stickiness: |
- | | instances, 0 for all | |
- | | other resources | .. index:: |
- | | | single: resource-stickiness; resource option |
- | | | single: resource; option, resource-stickiness |
- | | | |
- | | | A score that will be added 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. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | requires | ``quorum`` for resources | .. _requires: |
- | | with a ``class`` of ``stonith``, | |
- | | otherwise ``unfencing`` if | .. index:: |
- | | unfencing is active in the | single: requires; resource option |
- | | cluster, otherwise ``fencing`` | single: resource; option, requires |
- | | if ``stonith-enabled`` is true, | |
- | | otherwise ``quorum`` | Conditions under which the resource can be |
- | | | started. Allowed values: |
- | | | |
- | | | * ``nothing:`` can always be started |
- | | | * ``quorum:`` The cluster can only start this |
- | | | resource if a majority of the configured nodes |
- | | | are active |
- | | | * ``fencing:`` The cluster can only start this |
- | | | resource if a majority of the configured nodes |
- | | | are active *and* any failed or unknown nodes |
- | | | have been :ref:`fenced <fencing>` |
- | | | * ``unfencing:`` The cluster can only start this |
- | | | resource 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 |
- | | | :ref:`unfenced <unfencing>` |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | migration-threshold | INFINITY | .. index:: |
- | | | single: migration-threshold; resource option |
- | | | single: resource; option, migration-threshold |
- | | | |
- | | | How many failures may occur for this resource on |
- | | | a node, before this 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 constrast, the cluster treats |
- | | | INFINITY (the default) as a very large but finite |
- | | | number. This option has an effect only if the |
- | | | failed operation specifies ``on-fail`` as |
- | | | ``restart`` (the default), and additionally for |
- | | | failed ``start`` operations, if the cluster |
- | | | property ``start-failure-is-fatal`` is ``false``. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | failure-timeout | 0 | .. index:: |
- | | | single: failure-timeout; resource option |
- | | | single: resource; option, failure-timeout |
- | | | |
- | | | How many seconds to wait 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. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | multiple-active | stop_start | .. index:: |
- | | | single: multiple-active; resource option |
- | | | single: resource; option, multiple-active |
- | | | |
- | | | What should the cluster do if it ever finds the |
- | | | resource active on more than one node? Allowed |
- | | | values: |
- | | | |
- | | | * ``block``: mark the resource as unmanaged |
- | | | * ``stop_only``: stop all active instances and |
- | | | leave them that way |
- | | | * ``stop_start``: stop all active instances and |
- | | | start the resource in one location only |
- | | | * ``stop_unexpected``: stop all active instances |
- | | | 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 |
- | | | will still need to be restarted) *(since 2.1.3)* |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | allow-migrate | TRUE for ocf:pacemaker:remote | Whether the cluster should try to "live migrate" |
- | | resources, FALSE otherwise | this resource when it needs to be moved (see |
- | | | :ref:`live-migration`) |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | allow-unhealthy-nodes | FALSE | Whether the resource should be able to run on a node |
- | | | even if the node's health score would otherwise |
- | | | prevent it (see :ref:`node-health`) *(since 2.1.3)* |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | container-attribute-target | | Specific to bundle resources; see |
- | | | :ref:`s-bundle-attributes` |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | remote-node | | The 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. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | remote-port | 3121 | 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. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | remote-addr | value of ``remote-node`` | 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. |
- +----------------------------+----------------------------------+------------------------------------------------------+
- | remote-connect-timeout | 60s | If ``remote-node`` is specified, how long before |
- | | | a pending guest connection will time out. |
- +----------------------------+----------------------------------+------------------------------------------------------+
+ :widths: 2 2 3 5
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+
+ * - .. _meta_priority:
+
+ .. index::
+ single: priority; resource option
+ single: resource; option, priority
+
+ priority
+ - :ref:`score <score>`
+ - 0
+ - If not all resources can be active, the cluster will stop lower-priority
+ resources in order to keep higher-priority ones active.
+
+ * - .. _meta_critical:
+
+ .. index::
+ single: critical; resource option
+ single: resource; option, critical
+
+ critical
+ - :ref:`boolean <boolean>`
+ - true
+ - Use this value as the default for ``influence`` in all
+ :ref:`colocation constraints <s-resource-colocation>` involving this
+ resource, as well as in the implicit colocation constraints created if
+ this resource is in a :ref:`group <group-resources>`. For details, see
+ :ref:`s-coloc-influence`. *(since 2.1.0)*
+
+ * - .. _meta_target_role:
+
+ .. index::
+ single: target-role; resource option
+ single: resource; option, target-role
+
+ target-role
+ - :ref:`enumeration <enumeration>`
+ - Started
+ - What state should the cluster attempt to keep this resource in? Allowed
+ values:
+
+ * ``Stopped:`` Force the resource to be stopped
+ * ``Started:`` Allow the resource to be started (and in the case of
+ :ref:`promotable <s-resource-promotable>` clone resources, promoted if
+ appropriate)
+ * ``Unpromoted:`` Allow the resource to be started, but only in the
+ unpromoted role if the resource is
+ :ref:`promotable <s-resource-promotable>`
+ * ``Promoted:`` Equivalent to ``Started``
+
+ * - .. _meta_is_managed:
+ .. _is_managed:
+
+ .. index::
+ single: is-managed; resource option
+ single: resource; option, is-managed
+
+ is-managed
+ - :ref:`boolean <boolean>`
+ - true
+ - If false, the cluster will not start, stop, promote, or demote the
+ resource on any node. Recurring actions for the resource are
+ unaffected. Maintenance mode overrides this setting.
+
+ * - .. _meta_maintenance:
+ .. _rsc_maintenance:
+
+ .. index::
+ single: maintenance; resource option
+ single: resource; option, maintenance
+
+ maintenance
+ - :ref:`boolean <boolean>`
+ - false
+ - 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 true, the
+ :ref:`maintenance-mode <maintenance_mode>` cluster option or
+ :ref:`maintenance <node_maintenance>` node attribute overrides this.
+
+ * - .. _meta_resource_stickiness:
+ .. _resource-stickiness:
+
+ .. index::
+ single: resource-stickiness; resource option
+ single: resource; option, resource-stickiness
+
+ resource-stickiness
+ - :ref:`score <score>`
+ - 1 for individual clone instances, 0 for all other resources
+ - A score that will be added 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.
+
+ * - .. _meta_requires:
+ .. _requires:
+
+ .. index::
+ single: requires; resource option
+ single: resource; option, requires
+
+ requires
+ - :ref:`enumeration <enumeration>`
+ - ``quorum`` for resources with a ``class`` of ``stonith``, otherwise
+ ``unfencing`` if unfencing is active in the cluster, otherwise
+ ``fencing`` if ``stonith-enabled`` is true, otherwise ``quorum``
+ - Conditions under which the resource can be started. Allowed values:
+
+ * ``nothing:`` The cluster can always start this resource.
+ * ``quorum:`` The cluster can start this resource only if a majority of
+ the configured nodes are active.
+ * ``fencing:`` The cluster can start this resource only if a majority of
+ the configured nodes are active *and* any failed or unknown nodes have
+ been :ref:`fenced <fencing>`.
+ * ``unfencing:`` The cluster can only start this resource 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
+ :ref:`unfenced <unfencing>`.
+
+ * - .. _meta_migration_threshold:
+
+ .. index::
+ single: migration-threshold; resource option
+ single: resource; option, migration-threshold
+
+ migration-threshold
+ - :ref:`score <score>`
+ - INFINITY
+ - How many failures may occur for this resource on a node, before this 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 ``on-fail`` as ``restart`` (the default), and additionally for
+ failed ``start`` operations, if the cluster property
+ ``start-failure-is-fatal`` is ``false``.
+
+ * - .. _meta_failure_timeout:
+
+ .. index::
+ single: failure-timeout; resource option
+ single: resource; option, failure-timeout
+
+ failure-timeout
+ - :ref:`duration <duration>`
+ - 0
+ - How many seconds to wait 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.
+
+ * - .. _meta_multiple_active:
+
+ .. index::
+ single: multiple-active; resource option
+ single: resource; option, multiple-active
+
+ multiple-active
+ - :ref:`enumeration <enumeration>`
+ - stop_start
+ - What should the cluster do if it ever finds the resource active on more
+ than one node? Allowed values:
+
+ * ``block``: mark the resource as unmanaged
+ * ``stop_only``: stop all active instances and leave them that way
+ * ``stop_start``: stop all active instances and start the resource in one
+ location only
+ * ``stop_unexpected``: stop all active instances 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 will still need to be restarted) *(since 2.1.3)*
+
+ * - .. _meta_allow_migrate:
+
+ .. index::
+ single: allow-migrate; resource option
+ single: resource; option, allow-migrate
+
+ allow-migrate
+ - :ref:`boolean <boolean>`
+ - true for ``ocf:pacemaker:remote`` resources, false otherwise
+ - Whether the cluster should try to "live migrate" this resource when it
+ needs to be moved (see :ref:`live-migration`)
+
+ * - .. _meta_allow_unhealthy_nodes:
+
+ .. index::
+ single: allow-unhealthy-nodes; resource option
+ single: resource; option, allow-unhealthy-nodes
+
+ allow-unhealthy-nodes
+ - :ref:`boolean <boolean>`
+ - false
+ - Whether the resource should be able to run on a node even if the node's
+ health score would otherwise prevent it (see :ref:`node-health`) *(since
+ 2.1.3)*
+
+ * - .. _meta_container_attribute_target:
+
+ .. index::
+ single: container-attribute-target; resource option
+ single: resource; option, container-attribute-target
+
+ container-attribute-target
+ - :ref:`enumeration <enumeration>`
+ -
+ - Specific to bundle resources; see :ref:`s-bundle-attributes`
+
+ * - .. _meta_remote_node:
+
+ .. index::
+ single: remote-node; resource option
+ single: resource; option, remote-node
+
+ remote-node
+ - :ref:`text <text>`
+ -
+ - The 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.
+
+ * - .. _meta_remote_addr:
+
+ .. index::
+ single: remote-addr; resource option
+ single: resource; option, remote-addr
+
+ remote-addr
+ - :ref:`text <text>`
+ - value of ``remote-node``
+ - 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.
+
+ * - .. _meta_remote_port:
+
+ .. index::
+ single: remote-port; resource option
+ single: resource; option, remote-port
+
+ remote-port
+ - :ref:`port <port>`
+ - 3121
+ - 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.
+
+ * - .. _meta_remote_connect_timeout:
+
+ .. index::
+ single: remote-connect-timeout; resource option
+ single: resource; option, remote-connect-timeout
+
+ remote-connect-timeout
+ - :ref:`timeout <timeout>`
+ - 60s
+ - If ``remote-node`` is specified, how long before a pending guest
+ connection will time out.
+
+ * - .. _meta_remote_allow_migrate:
+
+ .. index::
+ single: remote-allow-migrate; resource option
+ single: resource; option, remote-allow-migrate
+
+ remote-allow-migrate
+ - :ref:`boolean <boolean>`
+ - true
+ - If ``remote-node`` is specified, this acts as the ``allow-migrate``
+ meta-attribute for the implicit remote connection resource
+ (``ocf:pacemaker:remote``).
+
As an example of setting resource options, if you performed the following
commands on an LSB Email resource:
diff --git a/doc/sphinx/Pacemaker_Explained/reusing-configuration.rst b/doc/sphinx/Pacemaker_Explained/reusing-configuration.rst
index 06c00f0..39f736f 100644
--- a/doc/sphinx/Pacemaker_Explained/reusing-configuration.rst
+++ b/doc/sphinx/Pacemaker_Explained/reusing-configuration.rst
@@ -262,10 +262,16 @@ Then instead of duplicating the rule for all your other resources, you can inste
.. important::
The cluster will insist that the ``rule`` exists somewhere. Attempting
- to add a reference to a non-existing rule will cause a validation
- failure, as will attempting to remove a ``rule`` that is referenced
+ to add a reference to a nonexistent ``id`` will cause a validation failure,
+ as will attempting to remove a ``rule`` with an ``id`` that is referenced
elsewhere.
+ Some rule syntax is allowed only in
+ :ref:`certain contexts <rule_conditions>`. Validation cannot ensure that the
+ referenced rule is allowed in the context of the rule containing ``id-ref``,
+ so such errors will be caught (and logged) only after the new configuration
+ is accepted. It is the administrator's reponsibility to check for these.
+
The same principle applies for ``meta_attributes`` and
``instance_attributes`` as illustrated in the example below:
@@ -288,7 +294,7 @@ The same principle applies for ``meta_attributes`` and
<op id="health-check" name="monitor" interval="30min"/>
</operations>
</primitive>
- <primitive id="myOtherlRsc" class="ocf" type="Other" provider="me">
+ <primitive id="myOtherRsc" class="ocf" type="Other" provider="me">
<instance_attributes id-ref="mySpecialRsc-attrs"/>
<meta_attributes id-ref="mySpecialRsc-options"/>
<operations id-ref="health-checks"/>
diff --git a/doc/sphinx/Pacemaker_Explained/rules.rst b/doc/sphinx/Pacemaker_Explained/rules.rst
index e9d85e0..13134da 100644
--- a/doc/sphinx/Pacemaker_Explained/rules.rst
+++ b/doc/sphinx/Pacemaker_Explained/rules.rst
@@ -6,226 +6,103 @@
Rules
-----
-Rules can be used to make your configuration more dynamic, allowing values to
-change depending on the time or the value of a node attribute. Examples of
-things rules are useful for:
+Rules make a configuration more dynamic, allowing values to depend on
+conditions such as time of day or the value of a node attribute. For example,
+rules can:
* Set a higher value for :ref:`resource-stickiness <resource-stickiness>`
- during working hours, to minimize downtime, and a lower value on weekends, to
+ during working hours to minimize downtime, and a lower value on weekends to
allow resources to move to their most preferred locations when people aren't
- around to notice.
+ around
* Automatically place the cluster into maintenance mode during a scheduled
- maintenance window.
+ maintenance window
-* Assign certain nodes and resources to a particular department via custom
- node attributes and meta-attributes, and add a single location constraint
- that restricts the department's resources to run only on those nodes.
-
-Each constraint type or property set that supports rules may contain one or more
-``rule`` elements specifying conditions under which the constraint or properties
-take effect. Examples later in this chapter will make this clearer.
+* Restrict a particular department's resources to run on certain nodes, as
+ determined by custom resource meta-attributes and node attributes
.. index::
- pair: XML element; rule
-
-Rule Properties
-###############
-
-.. table:: **Attributes of a rule Element**
- :widths: 1 1 3
-
- +-----------------+-------------+-------------------------------------------+
- | Attribute | Default | Description |
- +=================+=============+===========================================+
- | id | | .. index:: |
- | | | pair: rule; id |
- | | | |
- | | | A unique name for this element (required) |
- +-----------------+-------------+-------------------------------------------+
- | role | ``Started`` | .. index:: |
- | | | pair: rule; role |
- | | | |
- | | | The rule is in effect only when the |
- | | | resource is in the specified role. |
- | | | Allowed values are ``Started``, |
- | | | ``Unpromoted``, and ``Promoted``. A rule |
- | | | with a ``role`` of ``Promoted`` cannot |
- | | | determine the initial location of a clone |
- | | | instance and will only affect which of |
- | | | the active instances will be promoted. |
- +-----------------+-------------+-------------------------------------------+
- | score | | .. index:: |
- | | | pair: rule; score |
- | | | |
- | | | If this rule is used in a location |
- | | | constraint and evaluates to true, apply |
- | | | this score to the constraint. Only one of |
- | | | ``score`` and ``score-attribute`` may be |
- | | | used. |
- +-----------------+-------------+-------------------------------------------+
- | score-attribute | | .. index:: |
- | | | pair: rule; score-attribute |
- | | | |
- | | | If this rule is used in a location |
- | | | constraint and evaluates to true, use the |
- | | | value of this node attribute as the score |
- | | | to apply to the constraint. Only one of |
- | | | ``score`` and ``score-attribute`` may be |
- | | | used. |
- +-----------------+-------------+-------------------------------------------+
- | boolean-op | ``and`` | .. index:: |
- | | | pair: rule; boolean-op |
- | | | |
- | | | If this rule contains more than one |
- | | | condition, a value of ``and`` specifies |
- | | | that the rule evaluates to true only if |
- | | | all conditions are true, and a value of |
- | | | ``or`` specifies that the rule evaluates |
- | | | to true if any condition is true. |
- +-----------------+-------------+-------------------------------------------+
-
-A ``rule`` element must contain one or more conditions. A condition may be an
-``expression`` element, a ``date_expression`` element, or another ``rule`` element.
+ pair: rule; XML element
+ pair: rule; options
+Rule Options
+############
-.. index::
- single: rule; node attribute expression
- single: node attribute; rule expression
- pair: XML element; expression
+Each context that supports rules may contain a single ``rule`` element.
-.. _node_attribute_expressions:
+.. list-table:: **Attributes of a rule Element**
+ :class: longtable
+ :widths: 2 2 2 5
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+
+ * - .. _rule_id:
+
+ .. index::
+ pair: rule; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _boolean_op:
+
+ .. index::
+ pair: rule; boolean-op
+
+ boolean-op
+ - :ref:`enumeration <enumeration>`
+ - ``and``
+ - How to combine conditions if this rule contains more than one. Allowed
+ values:
+
+ * ``and``: the rule is satisfied only if all conditions are satisfied
+ * ``or``: the rule is satisfied if any condition is satisfied
+
+.. _rule_conditions:
-Node Attribute Expressions
-##########################
+.. index::
+ single: rule; conditions
+ single: rule; contexts
-Expressions are rule conditions based on the values of node attributes.
+Rule Conditions and Contexts
+############################
-.. table:: **Attributes of an expression Element**
- :class: longtable
- :widths: 1 2 3
-
- +--------------+---------------------------------+-------------------------------------------+
- | Attribute | Default | Description |
- +==============+=================================+===========================================+
- | id | | .. index:: |
- | | | pair: expression; id |
- | | | |
- | | | A unique name for this element (required) |
- +--------------+---------------------------------+-------------------------------------------+
- | attribute | | .. index:: |
- | | | pair: expression; attribute |
- | | | |
- | | | The node attribute to test (required) |
- +--------------+---------------------------------+-------------------------------------------+
- | type | The default type for | .. index:: |
- | | ``lt``, ``gt``, ``lte``, and | pair: expression; type |
- | | ``gte`` operations is ``number``| |
- | | if either value contains a | How the node attributes should be |
- | | decimal point character, or | compared. Allowed values are ``string``, |
- | | ``integer`` otherwise. The | ``integer`` *(since 2.0.5)*, ``number``, |
- | | default type for all other | and ``version``. ``integer`` truncates |
- | | operations is ``string``. If a | floating-point values if necessary before |
- | | numeric parse fails for either | performing a 64-bit integer comparison. |
- | | value, then the values are | ``number`` performs a double-precision |
- | | compared as type ``string``. | floating-point comparison |
- | | | *(32-bit integer before 2.0.5)*. |
- +--------------+---------------------------------+-------------------------------------------+
- | operation | | .. index:: |
- | | | pair: expression; operation |
- | | | |
- | | | The comparison to perform (required). |
- | | | Allowed values: |
- | | | |
- | | | * ``lt:`` True if the node attribute value|
- | | | is less than the comparison value |
- | | | * ``gt:`` True if the node attribute value|
- | | | is greater than the comparison value |
- | | | * ``lte:`` True if the node attribute |
- | | | value is less than or equal to the |
- | | | comparison value |
- | | | * ``gte:`` True if the node attribute |
- | | | value is greater than or equal to the |
- | | | comparison value |
- | | | * ``eq:`` True if the node attribute value|
- | | | is equal to the comparison value |
- | | | * ``ne:`` True if the node attribute value|
- | | | is not equal to the comparison value |
- | | | * ``defined:`` True if the node has the |
- | | | named attribute |
- | | | * ``not_defined:`` True if the node does |
- | | | not have the named attribute |
- +--------------+---------------------------------+-------------------------------------------+
- | value | | .. index:: |
- | | | pair: expression; value |
- | | | |
- | | | User-supplied value for comparison |
- | | | (required for operations other than |
- | | | ``defined`` and ``not_defined``) |
- +--------------+---------------------------------+-------------------------------------------+
- | value-source | ``literal`` | .. index:: |
- | | | pair: expression; value-source |
- | | | |
- | | | How the ``value`` is derived. Allowed |
- | | | values: |
- | | | |
- | | | * ``literal``: ``value`` is a literal |
- | | | string to compare against |
- | | | * ``param``: ``value`` is the name of a |
- | | | resource parameter to compare against |
- | | | (only valid in location constraints) |
- | | | * ``meta``: ``value`` is the name of a |
- | | | resource meta-attribute to compare |
- | | | against (only valid in location |
- | | | constraints) |
- +--------------+---------------------------------+-------------------------------------------+
+A ``rule`` element must contain one or more conditions. A condition is any of
+the following, which will be described in more detail later:
-.. _node-attribute-expressions-special:
+* a :ref:`date/time expression <date_expression>`
+* a :ref:`node attribute expression <node_attribute_expressions>`
+* a :ref:`resource type expression <rsc_expression>`
+* an :ref:`operation type expression <op_expression>`
+* another ``rule`` (allowing for complex combinations of conditions)
-In addition to custom node attributes defined by the administrator, the cluster
-defines special, built-in node attributes for each node that can also be used
-in rule expressions.
+Each type of condition is allowed only in certain contexts. Although any given
+context may contain only one ``rule`` element, that element may contain any
+number of conditions, including other ``rule`` elements.
-.. table:: **Built-in Node Attributes**
- :widths: 1 4
+Rules may be used in the following contexts, which also will be described in
+more detail later:
- +---------------+-----------------------------------------------------------+
- | Name | Value |
- +===============+===========================================================+
- | #uname | :ref:`Node name <node_name>` |
- +---------------+-----------------------------------------------------------+
- | #id | Node ID |
- +---------------+-----------------------------------------------------------+
- | #kind | Node type. Possible values are ``cluster``, ``remote``, |
- | | and ``container``. Kind is ``remote`` for Pacemaker Remote|
- | | nodes created with the ``ocf:pacemaker:remote`` resource, |
- | | and ``container`` for Pacemaker Remote guest nodes and |
- | | bundle nodes |
- +---------------+-----------------------------------------------------------+
- | #is_dc | ``true`` if this node is the cluster's Designated |
- | | Controller (DC), ``false`` otherwise |
- +---------------+-----------------------------------------------------------+
- | #cluster-name | The value of the ``cluster-name`` cluster property, if set|
- +---------------+-----------------------------------------------------------+
- | #site-name | The value of the ``site-name`` node attribute, if set, |
- | | otherwise identical to ``#cluster-name`` |
- +---------------+-----------------------------------------------------------+
- | #role | The role the relevant promotable clone resource has on |
- | | this node. Valid only within a rule for a location |
- | | constraint for a promotable clone resource. |
- +---------------+-----------------------------------------------------------+
-
-.. Add_to_above_table_if_released:
-
- +---------------+-----------------------------------------------------------+
- | #ra-version | The installed version of the resource agent on the node, |
- | | as defined by the ``version`` attribute of the |
- | | ``resource-agent`` tag in the agent's metadata. Valid only|
- | | within rules controlling resource options. This can be |
- | | useful during rolling upgrades of a backward-incompatible |
- | | resource agent. *(since x.x.x)* |
+* a :ref:`location constraint <location_rule>`
+* a :ref:`cluster_property_set <cluster_options>` element (within the
+ ``crm_config`` element)
+* an :ref:`instance_attributes <option_rule>` element (within an ``alert``,
+ ``bundle``, ``clone``, ``group``, ``node``, ``op``, ``primitive``,
+ ``recipient``, or ``template`` element)
+* a :ref:`meta_attributes <option_rule>` element (within an ``alert``,
+ ``bundle``, ``clone``, ``group``, ``op``, ``op_defaults``, ``primitive``,
+ ``recipient``, ``rsc_defaults``, or ``template`` element)
+* a :ref:`utilization <option_rule>` element (within a ``node``, ``primitive``,
+ or ``template`` element)
+.. _date_expression:
+
.. index::
single: rule; date/time expression
pair: XML element; date_expression
@@ -233,66 +110,77 @@ in rule expressions.
Date/Time Expressions
#####################
-Date/time expressions are rule conditions based (as the name suggests) on the
-current date and time.
+The ``date_expression`` element configures a rule condition based on the
+current date and time. It is allowed in rules in any context.
-A ``date_expression`` element may optionally contain a ``date_spec`` or
-``duration`` element depending on the context.
-
-.. table:: **Attributes of a date_expression Element**
- :widths: 1 4
-
- +---------------+-----------------------------------------------------------+
- | Attribute | Description |
- +===============+===========================================================+
- | id | .. index:: |
- | | pair: id; date_expression |
- | | |
- | | A unique name for this element (required) |
- +---------------+-----------------------------------------------------------+
- | start | .. index:: |
- | | pair: start; date_expression |
- | | |
- | | A date/time conforming to the |
- | | `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ |
- | | specification. May be used when ``operation`` is |
- | | ``in_range`` (in which case at least one of ``start`` or |
- | | ``end`` must be specified) or ``gt`` (in which case |
- | | ``start`` is required). |
- +---------------+-----------------------------------------------------------+
- | end | .. index:: |
- | | pair: end; date_expression |
- | | |
- | | A date/time conforming to the |
- | | `ISO8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ |
- | | specification. May be used when ``operation`` is |
- | | ``in_range`` (in which case at least one of ``start`` or |
- | | ``end`` must be specified) or ``lt`` (in which case |
- | | ``end`` is required). |
- +---------------+-----------------------------------------------------------+
- | operation | .. index:: |
- | | pair: operation; date_expression |
- | | |
- | | Compares the current date/time with the start and/or end |
- | | date, depending on the context. Allowed values: |
- | | |
- | | * ``gt:`` True if the current date/time is after ``start``|
- | | * ``lt:`` True if the current date/time is before ``end`` |
- | | * ``in_range:`` True if the current date/time is after |
- | | ``start`` (if specified) and before either ``end`` (if |
- | | specified) or ``start`` plus the value of the |
- | | ``duration`` element (if one is contained in the |
- | | ``date_expression``). If both ``end`` and ``duration`` |
- | | are specified, ``duration`` is ignored. |
- | | * ``date_spec:`` True if the current date/time matches |
- | | the specification given in the contained ``date_spec`` |
- | | element (described below) |
- +---------------+-----------------------------------------------------------+
-
-
-.. note:: There is no ``eq``, ``neq``, ``gte``, or ``lte`` operation, since
- they would be valid only for a single second.
+It may contain a ``date_spec`` or ``duration`` element depending on the
+``operation`` as described below.
+.. list-table:: **Attributes of a date_expression Element**
+ :class: longtable
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _date_expression_id:
+
+ .. index::
+ pair: date_expression; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _date_expression_start:
+
+ .. index::
+ pair: date_expression; start
+
+ start
+ - :ref:`ISO 8601 <iso8601>`
+ -
+ - The beginning of the desired time range. Meaningful with an
+ ``operation`` of ``in_range`` or ``gt``.
+ * - .. _date_expression_end:
+
+ .. index::
+ pair: date_expression; end
+
+ end
+ - :ref:`ISO 8601 <iso8601>`
+ -
+ - The end of the desired time range. Meaningful with an ``operation`` of
+ ``in_range`` or ``lt``.
+ * - .. _date_expression_operation:
+
+ .. index::
+ pair: date_expression; operation
+
+ operation
+ - :ref:`enumeration <enumeration>`
+ - ``in_range``
+ - Specifies how to compare the current date/time against a desired time
+ range. Allowed values:
+
+ * ``gt:`` The expression is satisfied if the current date/time is after
+ ``start`` (which is required)
+ * ``lt:`` The expression is satisfied if the current date/time is before
+ ``end`` (which is required)
+ * ``in_range:`` The expression is satisfied if the current date/time is
+ greater than or equal to ``start`` (if specified) and less than or
+ equal to either ``end`` (if specified) or ``start`` plus the value of
+ the :ref:`duration <duration_element>` element (if one is contained in
+ the ``date_expression``). At least one of ``start`` or ``end`` must be
+ specified. If both ``end`` and ``duration`` are specified,
+ ``duration`` is ignored.
+ * ``date_spec:`` The expression is satisfied if the current date/time
+ matches the specification given in the contained
+ :ref:`date_spec <date_spec>` element (which is required)
+
+.. _date_spec:
.. index::
single: date specification
@@ -301,87 +189,142 @@ A ``date_expression`` element may optionally contain a ``date_spec`` or
Date Specifications
___________________
-A ``date_spec`` element is used to create a cron-like expression relating
-to time. Each field can contain a single number or range. Any field not
-supplied is ignored.
-
-.. table:: **Attributes of a date_spec Element**
- :widths: 1 3
-
- +---------------+-----------------------------------------------------------+
- | Attribute | Description |
- +===============+===========================================================+
- | id | .. index:: |
- | | pair: id; date_spec |
- | | |
- | | A unique name for this element (required) |
- +---------------+-----------------------------------------------------------+
- | seconds | .. index:: |
- | | pair: seconds; date_spec |
- | | |
- | | Allowed values: 0-59 |
- +---------------+-----------------------------------------------------------+
- | minutes | .. index:: |
- | | pair: minutes; date_spec |
- | | |
- | | Allowed values: 0-59 |
- +---------------+-----------------------------------------------------------+
- | hours | .. index:: |
- | | pair: hours; date_spec |
- | | |
- | | Allowed values: 0-23 (where 0 is midnight and 23 is |
- | | 11 p.m.) |
- +---------------+-----------------------------------------------------------+
- | monthdays | .. index:: |
- | | pair: monthdays; date_spec |
- | | |
- | | Allowed values: 1-31 (depending on month and year) |
- +---------------+-----------------------------------------------------------+
- | weekdays | .. index:: |
- | | pair: weekdays; date_spec |
- | | |
- | | Allowed values: 1-7 (where 1 is Monday and 7 is Sunday) |
- +---------------+-----------------------------------------------------------+
- | yeardays | .. index:: |
- | | pair: yeardays; date_spec |
- | | |
- | | Allowed values: 1-366 (depending on the year) |
- +---------------+-----------------------------------------------------------+
- | months | .. index:: |
- | | pair: months; date_spec |
- | | |
- | | Allowed values: 1-12 |
- +---------------+-----------------------------------------------------------+
- | weeks | .. index:: |
- | | pair: weeks; date_spec |
- | | |
- | | Allowed values: 1-53 (depending on weekyear) |
- +---------------+-----------------------------------------------------------+
- | years | .. index:: |
- | | pair: years; date_spec |
- | | |
- | | Year according to the Gregorian calendar |
- +---------------+-----------------------------------------------------------+
- | weekyears | .. index:: |
- | | pair: weekyears; date_spec |
- | | |
- | | Year in which the week started; for example, 1 January |
- | | 2005 can be specified in ISO 8601 as "2005-001 Ordinal", |
- | | "2005-01-01 Gregorian" or "2004-W53-6 Weekly" and thus |
- | | would match ``years="2005"`` or ``weekyears="2004"`` |
- +---------------+-----------------------------------------------------------+
- | moon | .. index:: |
- | | pair: moon; date_spec |
- | | |
- | | Allowed values are 0-7 (where 0 is the new moon and 4 is |
- | | full moon). *(deprecated since 2.1.6)* |
- +---------------+-----------------------------------------------------------+
-
-For example, ``monthdays="1"`` matches the first day of every month, and
-``hours="09-17"`` matches the hours between 9 a.m. and 5 p.m. (inclusive).
-
-At this time, multiple ranges (e.g. ``weekdays="1,2"`` or ``weekdays="1-2,5-6"``)
-are not supported.
+A ``date_spec`` element is used within a ``date_expression`` to specify a
+combination of dates and times that satisfy the expression.
+
+.. list-table:: **Attributes of a date_spec Element**
+ :class: longtable
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _date_spec_id:
+
+ .. index::
+ pair: date_spec; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _date_spec_seconds:
+
+ .. index::
+ pair: date_spec; seconds
+
+ seconds
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current time's
+ second is within this range. Allowed integers: 0 to 59.
+ * - .. _date_spec_minutes:
+
+ .. index::
+ pair: date_spec; minutes
+
+ minutes
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current time's
+ minute is within this range. Allowed integers: 0 to 59.
+ * - .. _date_spec_hours:
+
+ .. index::
+ pair: date_spec; hours
+
+ hours
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current time's
+ hour is within this range. Allowed integers: 0 to 23 where 0 is midnight
+ and 23 is 11 p.m.
+ * - .. _date_spec_monthdays:
+
+ .. index::
+ pair: date_spec; monthdays
+
+ monthdays
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ day of the month is in this range. Allowed integers: 1 to 31.
+ * - .. _date_spec_weekdays:
+
+ .. index::
+ pair: date_spec; weekdays
+
+ weekdays
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ ordinal day of the week is in this range. Allowed integers: 1-7 (where 1
+ is Monday and 7 is Sunday).
+ * - .. _date_spec_yeardays:
+
+ .. index::
+ pair: date_spec; yeardays
+
+ yeardays
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ ordinal day of the year is in this range. Allowed integers: 1-366.
+ * - .. _date_spec_months:
+
+ .. index::
+ pair: date_spec; months
+
+ months
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ month is in this range. Allowed integers: 1-12 where 1 is January and 12
+ is December.
+ * - .. _date_spec_weeks:
+
+ .. index::
+ pair: date_spec; weeks
+
+ weeks
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ ordinal week of the year is in this range. Allowed integers: 1-53.
+ * - .. _date_spec_years:
+
+ .. index::
+ pair: date_spec; years
+
+ years
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ year according to the Gregorian calendar is in this range.
+ * - .. _date_spec_weekyears:
+
+ .. index::
+ pair: date_spec; weekyears
+
+ weekyears
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ year in which the week started (according to the ISO 8601 standard) is
+ in this range.
+ * - .. _date_spec_moon:
+
+ .. index::
+ pair: date_spec; moon
+
+ moon
+ - :ref:`range <range>`
+ -
+ - If this is set, the expression is satisfied only if the current date's
+ phase of the moon is in this range. Allowed values are 0 to 7 where 0 is
+ the new moon and 4 is the full moon. *(deprecated since 2.1.6)*
.. note:: Pacemaker can calculate when evaluation of a ``date_expression`` with
an ``operation`` of ``gt``, ``lt``, or ``in_range`` will next change,
@@ -400,6 +343,8 @@ are not supported.
need to perform first, and the load of the machine.
+.. _duration_element:
+
.. index::
single: duration
pair: XML element; duration
@@ -407,64 +352,97 @@ are not supported.
Durations
_________
-A ``duration`` is used to calculate a value for ``end`` when one is not
-supplied to ``in_range`` operations. It contains one or more attributes each
-containing a single number. Any attribute not supplied is ignored.
-
-.. table:: **Attributes of a duration Element**
- :widths: 1 3
-
- +---------------+-----------------------------------------------------------+
- | Attribute | Description |
- +===============+===========================================================+
- | id | .. index:: |
- | | pair: id; duration |
- | | |
- | | A unique name for this element (required) |
- +---------------+-----------------------------------------------------------+
- | seconds | .. index:: |
- | | pair: seconds; duration |
- | | |
- | | This many seconds will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | minutes | .. index:: |
- | | pair: minutes; duration |
- | | |
- | | This many minutes will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | hours | .. index:: |
- | | pair: hours; duration |
- | | |
- | | This many hours will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | days | .. index:: |
- | | pair: days; duration |
- | | |
- | | This many days will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | weeks | .. index:: |
- | | pair: weeks; duration |
- | | |
- | | This many weeks will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | months | .. index:: |
- | | pair: months; duration |
- | | |
- | | This many months will be added to the total duration |
- +---------------+-----------------------------------------------------------+
- | years | .. index:: |
- | | pair: years; duration |
- | | |
- | | This many years will be added to the total duration |
- +---------------+-----------------------------------------------------------+
-
-
-Example Time-Based Expressions
-______________________________
-
-A small sample of how time-based expressions can be used:
-
-.. topic:: True if now is any time in the year 2005
+A ``duration`` element is used within a ``date_expression`` to calculate an
+ending value for ``in_range`` operations when ``end`` is not supplied.
+
+.. list-table:: **Attributes of a duration Element**
+ :class: longtable
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _duration_id:
+
+ .. index::
+ pair: duration; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _duration_seconds:
+
+ .. index::
+ pair: duration; seconds
+
+ seconds
+ - :ref:`integer <integer>`
+ - 0
+ - Number of seconds to add to the total duration
+ * - .. _duration_minutes:
+
+ .. index::
+ pair: duration; minutes
+
+ minutes
+ - :ref:`integer <integer>`
+ - 0
+ - Number of minutes to add to the total duration
+ * - .. _duration_hours:
+
+ .. index::
+ pair: duration; hours
+
+ hours
+ - :ref:`integer <integer>`
+ - 0
+ - Number of hours to add to the total duration
+ * - .. _duration_days:
+
+ .. index::
+ pair: duration; days
+
+ days
+ - :ref:`integer <integer>`
+ - 0
+ - Number of days to add to the total duration
+ * - .. _duration_weeks:
+
+ .. index::
+ pair: duration; weeks
+
+ weeks
+ - :ref:`integer <integer>`
+ - 0
+ - Number of weeks to add to the total duration
+ * - .. _duration_months:
+
+ .. index::
+ pair: duration; months
+
+ months
+ - :ref:`integer <integer>`
+ - 0
+ - Number of months to add to the total duration
+ * - .. _duration_years:
+
+ .. index::
+ pair: duration; years
+
+ years
+ - :ref:`integer <integer>`
+ - 0
+ - Number of years to add to the total duration
+
+
+Example Date/Time Expressions
+_____________________________
+
+
+.. topic:: Satisfied if the current year is 2005
.. code-block:: xml
@@ -497,7 +475,7 @@ A small sample of how time-based expressions can be used:
Note that the ``16`` matches all the way through ``16:59:59``, because the
numeric value of the hour still matches.
-.. topic:: 9 a.m. to 6 p.m. Monday through Friday or anytime Saturday
+.. topic:: 9 a.m. to 6 p.m. Monday through Friday, or anytime Saturday
.. code-block:: xml
@@ -538,63 +516,227 @@ A small sample of how time-based expressions can be used:
</date_expression>
<date_expression id="date_expr6-2" operation="in_range"
start="2005-03-01" end="2005-04-01"/>
+ </date_expression>
</rule>
.. note:: Because no time is specified with the above dates, 00:00:00 is
implied. This means that the range includes all of 2005-03-01 but
- none of 2005-04-01. You may wish to write ``end`` as
- ``"2005-03-31T23:59:59"`` to avoid confusion.
+ only the first second of 2005-04-01. You may wish to write ``end``
+ as ``"2005-03-31T23:59:59"`` to avoid confusion.
.. index::
+ single: rule; node attribute expression
+ single: node attribute; rule expression
+ pair: XML element; expression
+
+.. _node_attribute_expressions:
+
+Node Attribute Expressions
+##########################
+
+The ``expression`` element configures a rule condition based on the value of a
+node attribute. It is allowed in rules in location constraints and in
+``instance_attributes`` elements within ``bundle``, ``clone``, ``group``,
+``op``, ``primitive``, and ``template`` elements.
+
+.. list-table:: **Attributes of an expression Element**
+ :class: longtable
+ :widths: 1 1 3 5
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+
+ * - .. _expression_id:
+
+ .. index::
+ pair: expression; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _expression_attribute:
+
+ .. index::
+ pair: expression; attribute
+
+ attribute
+ - :ref:`text <text>`
+ -
+ - Name of the node attribute to test (required)
+ * - .. _expression_operation:
+
+ .. index::
+ pair: expression; operation
+
+ operation
+ - :ref:`enumeration <enumeration>`
+ -
+ - The comparison to perform (required). Allowed values:
+
+ * ``defined:`` The expression is satisfied if the node has the named
+ attribute
+ * ``not_defined:`` The expression is satisfied if the node does not have
+ the named attribute
+ * ``lt:`` The expression is satisfied if the node attribute value is
+ less than the reference value
+ * ``gt:`` The expression is satisfied if the node attribute value is
+ greater than the reference value
+ * ``lte:`` The expression is satisfied if the node attribute value is
+ less than or equal to the reference value
+ * ``gte:`` The expression is satisfied if the node attribute value is
+ greater than or equal to the reference value
+ * ``eq:`` The expression is satisfied if the node attribute value is
+ equal to the reference value
+ * ``ne:`` The expression is satisfied if the node attribute value is not
+ equal to the reference value
+ * - .. _expression_type:
+
+ .. index::
+ pair: expression; type
+
+ type
+ - :ref:`enumeration <enumeration>`
+ - The default type for ``lt``, ``gt``, ``lte``, and ``gte`` operations is
+ ``number`` if either value contains a decimal point character, or
+ ``integer`` otherwise. The default type for all other operations is
+ ``string``. If a numeric parse fails for either value, then the values
+ are compared as type ``string``.
+ - How to interpret values. Allowed values are ``string``, ``integer``
+ *(since 2.0.5)*, ``number``, and ``version``. ``integer`` truncates
+ floating-point values if necessary before performing a 64-bit integer
+ comparison. ``number`` performs a double-precision floating-point
+ comparison *(32-bit integer before 2.0.5)*.
+ * - .. _expression_value:
+
+ .. index::
+ pair: expression; value
+
+ value
+ - :ref:`text <text>`
+ -
+ - Reference value to compare node attribute against (used only with, and
+ required for, operations other than ``defined`` and ``not_defined``)
+ * - .. _expression_value_source:
+
+ .. index::
+ pair: expression; value-source
+
+ value-source
+ - :ref:`enumeration <enumeration>`
+ - ``literal``
+ - How the reference value is obtained. Allowed values:
+
+ * ``literal``: ``value`` contains the literal reference value to compare
+ * ``param``: ``value`` contains the name of a resource parameter to
+ compare (valid only in the context of a location constraint)
+ * ``meta``: ``value`` is the name of a resource meta-attribute to
+ compare (valid only in the context of a location constraint)
+
+.. _node-attribute-expressions-special:
+
+In addition to custom node attributes defined by the administrator, the cluster
+defines special, built-in node attributes for each node that can also be used
+in rule expressions.
+
+.. list-table:: **Built-in Node Attributes**
+ :class: longtable
+ :widths: 1 4
+ :header-rows: 1
+
+ * - Name
+ - Description
+ * - #uname
+ - :ref:`Node name <node_name>`
+ * - #id
+ - Node ID
+ * - #kind
+ - Node type (``cluster`` for cluster nodes, ``remote`` for Pacemaker
+ Remote nodes created with the ``ocf:pacemaker:remote`` resource, and
+ ``container`` for Pacemaker Remote guest nodes and bundle nodes)
+ * - #is_dc
+ - ``true`` if this node is the cluster's Designated Controller (DC),
+ ``false`` otherwise
+ * - #cluster-name
+ - The value of the ``cluster-name`` cluster property, if set
+ * - #site-name
+ - The value of the ``site-name`` node attribute, if set, otherwise
+ identical to ``#cluster-name``
+
+
+.. _rsc_expression:
+
+.. index::
single: rule; resource expression
single: resource; rule expression
pair: XML element; rsc_expression
-Resource Expressions
-####################
-
-An ``rsc_expression`` *(since 2.0.5)* is a rule condition based on a resource
-agent's properties. This rule is only valid within an ``rsc_defaults`` or
-``op_defaults`` context. None of the matching attributes of ``class``,
-``provider``, and ``type`` are required. If one is omitted, all values of that
-attribute will match. For instance, omitting ``type`` means every type will
-match.
-
-.. table:: **Attributes of a rsc_expression Element**
- :widths: 1 3
-
- +---------------+-----------------------------------------------------------+
- | Attribute | Description |
- +===============+===========================================================+
- | id | .. index:: |
- | | pair: id; rsc_expression |
- | | |
- | | A unique name for this element (required) |
- +---------------+-----------------------------------------------------------+
- | class | .. index:: |
- | | pair: class; rsc_expression |
- | | |
- | | The standard name to be matched against resource agents |
- +---------------+-----------------------------------------------------------+
- | provider | .. index:: |
- | | pair: provider; rsc_expression |
- | | |
- | | If given, the vendor to be matched against resource |
- | | agents (only relevant when ``class`` is ``ocf``) |
- +---------------+-----------------------------------------------------------+
- | type | .. index:: |
- | | pair: type; rsc_expression |
- | | |
- | | The name of the resource agent to be matched |
- +---------------+-----------------------------------------------------------+
-
-Example Resource-Based Expressions
-__________________________________
+Resource Type Expressions
+#########################
+
+The ``rsc_expression`` element *(since 2.0.5)* configures a rule condition
+based on the agent used for a resource. It is allowed in rules in a
+``meta_attributes`` element within a ``rsc_defaults`` or ``op_defaults``
+element.
+
+.. list-table:: **Attributes of a rsc_expression Element**
+ :class: longtable
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _rsc_expression_id:
+
+ .. index::
+ pair: rsc_expression; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _rsc_expression_class:
+
+ .. index::
+ pair: rsc_expression; class
+
+ class
+ - :ref:`text <text>`
+ -
+ - If this is set, the expression is satisfied only if the resource's agent
+ standard matches this value
+ * - .. _rsc_expression_provider:
+
+ .. index::
+ pair: rsc_expression; provider
+
+ provider
+ - :ref:`text <text>`
+ -
+ - If this is set, the expression is satisfied only if the resource's agent
+ provider matches this value
+ * - .. _rsc_expression_type:
+
+ .. index::
+ pair: rsc_expression; type
-A small sample of how resource-based expressions can be used:
+ type
+ - :ref:`text <text>`
+ -
+ - If this is set, the expression is satisfied only if the resource's agent
+ type matches this value
-.. topic:: True for all ``ocf:heartbeat:IPaddr2`` resources
+
+Example Resource Type Expressions
+_________________________________
+
+.. topic:: Satisfied for ``ocf:heartbeat:IPaddr2`` resources
.. code-block:: xml
@@ -602,7 +744,7 @@ A small sample of how resource-based expressions can be used:
<rsc_expression id="rule_expr1" class="ocf" provider="heartbeat" type="IPaddr2"/>
</rule>
-.. topic:: Provider doesn't apply to non-OCF resources
+.. topic:: Satisfied for ``stonith:fence_xvm`` resources
.. code-block:: xml
@@ -611,49 +753,64 @@ A small sample of how resource-based expressions can be used:
</rule>
+.. _op_expression:
+
.. index::
single: rule; operation expression
single: operation; rule expression
pair: XML element; op_expression
-Operation Expressions
-#####################
+Operation Type Expressions
+##########################
+
+The ``op_expression`` element *(since 2.0.5)* configures a rule condition based
+on a resource operation name and interval. It is allowed in rules in a
+``meta_attributes`` element within an ``op_defaults`` element.
+
+.. list-table:: **Attributes of an op_expression Element**
+ :class: longtable
+ :widths: 1 1 1 4
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+ * - .. _op_expression_id:
+
+ .. index::
+ pair: op_expression; id
+
+ id
+ - :ref:`id <id>`
+ -
+ - A unique name for this element (required)
+ * - .. _op_expression_name:
+
+ .. index::
+ pair: op_expression; name
+
+ name
+ - :ref:`text <text>`
+ -
+ - The expression is satisfied only if the operation's name matches this
+ value (required)
+ * - .. _op_expression_interval:
+
+ .. index::
+ pair: op_expression; interval
+ interval
+ - :ref:`duration <duration>`
+ -
+ - If this is set, the expression is satisfied only if the operation's
+ interval matches this value
-An ``op_expression`` *(since 2.0.5)* is a rule condition based on an action of
-some resource agent. This rule is only valid within an ``op_defaults`` context.
-
-.. table:: **Attributes of an op_expression Element**
- :widths: 1 3
-
- +---------------+-----------------------------------------------------------+
- | Attribute | Description |
- +===============+===========================================================+
- | id | .. index:: |
- | | pair: id; op_expression |
- | | |
- | | A unique name for this element (required) |
- +---------------+-----------------------------------------------------------+
- | name | .. index:: |
- | | pair: name; op_expression |
- | | |
- | | The action name to match against. This can be any action |
- | | supported by the resource agent; common values include |
- | | ``monitor``, ``start``, and ``stop`` (required). |
- +---------------+-----------------------------------------------------------+
- | interval | .. index:: |
- | | pair: interval; op_expression |
- | | |
- | | The interval of the action to match against. If not given,|
- | | only the name attribute will be used to match. |
- +---------------+-----------------------------------------------------------+
-
-Example Operation-Based Expressions
-___________________________________
-
-A small sample of how operation-based expressions can be used:
-
-.. topic:: True for all monitor actions
+
+Example Operation Type Expressions
+__________________________________
+
+.. topic:: Expression is satisfied for all monitor actions
.. code-block:: xml
@@ -661,7 +818,7 @@ A small sample of how operation-based expressions can be used:
<op_expression id="rule_expr1" name="monitor"/>
</rule>
-.. topic:: True for all monitor actions with a 10 second interval
+.. topic:: Expression is satisfied for all monitor actions with a 10-second interval
.. code-block:: xml
@@ -670,15 +827,68 @@ A small sample of how operation-based expressions can be used:
</rule>
+.. _location_rule:
+
.. index::
pair: location constraint; rule
Using Rules to Determine Resource Location
##########################################
-A location constraint may contain one or more top-level rules. The cluster will
-act as if there is a separate location constraint for each rule that evaluates
-as true.
+If a :ref:`location constraint <location-constraint>` contains a rule, the
+cluster will apply the constraint to all nodes where the rule is satisfied.
+This acts as if identical location constraints without rules were defined for
+each of the nodes.
+
+In the context of a location constraint, ``rule`` elements may take additional
+attributes. These have an effect only when set for the constraint's top-level
+``rule``; they are ignored if set on a subrule.
+
+.. list-table:: **Extra Attributes of a rule Element in a Location Constraint**
+ :class: longtable
+ :widths: 2 2 1 5
+ :header-rows: 1
+
+ * - Name
+ - Type
+ - Default
+ - Description
+
+ * - .. _rule_role:
+
+ .. index::
+ pair: rule; role
+
+ role
+ - :ref:`enumeration <enumeration>`
+ - ``Started``
+ - If this is set in the constraint's top-level rule, the constraint acts
+ as if ``role`` were set to this in the ``rsc_location`` element.
+
+ * - .. _rule_score:
+
+ .. index::
+ pair: rule; score
+
+ score
+ - :ref:`score <score>`
+ -
+ - If this is set in the constraint's top-level rule, the constraint acts
+ as if ``score`` were set to this in the ``rsc_location`` element.
+ Only one of ``score`` and ``score-attribute`` may be set.
+
+ * - .. _rule_score_attribute:
+
+ .. index::
+ pair: rule; score-attribute
+
+ score-attribute
+ - :ref:`text <text>`
+ -
+ - If this is set in the constraint's top-level rule, the constraint acts
+ as if ``score`` were set to the value of this node attribute on each
+ node where the rule is satisfied. Only one of ``score`` and
+ ``score-attribute`` may be set.
Consider the following simple location constraint:
@@ -689,7 +899,7 @@ Consider the following simple location constraint:
<rsc_location id="ban-apache-on-node3" rsc="webserver"
score="-INFINITY" node="node3"/>
-The same constraint can be more verbosely written using a rule:
+The same constraint can be written more verbosely using a rule:
.. topic:: Prevent resource ``webserver`` from running on node ``node3`` using a rule
@@ -703,15 +913,14 @@ The same constraint can be more verbosely written using a rule:
</rsc_location>
The advantage of using the expanded form is that one could add more expressions
-(for example, limiting the constraint to certain days of the week), or activate
-the constraint by some node attribute other than node name.
+(for example, limiting the constraint to certain days of the week).
Location Rules Based on Other Node Properties
_____________________________________________
-The expanded form allows us to match on node properties other than its name.
-If we rated each machine's CPU power such that the cluster had the following
-nodes section:
+The expanded form allows us to match node attributes other than its name. As an
+example, consider this configuration of custom node attributes specifying each
+node's CPU capacity:
.. topic:: Sample node section with node attributes
@@ -730,8 +939,7 @@ nodes section:
</node>
</nodes>
-then we could prevent resources from running on underpowered machines with this
-rule:
+We can use a rule to prevent a resource from running on underpowered machines:
.. topic:: Rule using a node attribute (to be used inside a location constraint)
@@ -746,11 +954,13 @@ Using ``score-attribute`` Instead of ``score``
______________________________________________
When using ``score-attribute`` instead of ``score``, each node matched by the
-rule has its score adjusted differently, according to its value for the named
-node attribute. Thus, in the previous example, if a rule inside a location
-constraint for a resource used ``score-attribute="cpu_mips"``, ``c001n01``
-would have its preference to run the resource increased by ``1234`` whereas
-``c001n02`` would have its preference increased by ``5678``.
+rule has its score adjusted according to its value for the named node
+attribute.
+
+In the previous example, if the location constraint rule used
+``score-attribute="cpu_mips"`` instead of ``score="-INFINITY"``, node
+``c001n01`` would have its preference to run the resource increased by 1234
+whereas node ``c001n02`` would have its preference increased by 5678.
.. _s-rsc-pattern-rules:
@@ -758,16 +968,15 @@ would have its preference to run the resource increased by ``1234`` whereas
Specifying location scores using pattern submatches
___________________________________________________
-Location constraints may use ``rsc-pattern`` to apply the constraint to all
-resources whose IDs match the given pattern (see :ref:`s-rsc-pattern`). The
-pattern may contain up to 9 submatches in parentheses, whose values may be used
-as ``%1`` through ``%9`` in a rule's ``score-attribute`` or a rule expression's
-``attribute``.
+Location constraints may use :ref:`rsc-pattern <s-rsc-pattern>` to apply the
+constraint to all resources whose IDs match the given pattern. The pattern may
+contain up to 9 submatches in parentheses, whose values may be used as ``%1``
+through ``%9`` in a ``rule`` element's ``score-attribute`` or an ``expression``
+element's ``attribute``.
-As an example, the following configuration (only relevant parts are shown)
-gives the resources **server-httpd** and **ip-httpd** a preference of 100 on
-**node1** and 50 on **node2**, and **ip-gateway** a preference of -100 on
-**node1** and 200 on **node2**.
+For example, the following configuration excerpt gives the resources
+**server-httpd** and **ip-httpd** a preference of 100 on node1 and 50 on node2,
+and **ip-gateway** a preference of -100 on node1 and 200 on node2.
.. topic:: Location constraint using submatches
@@ -807,6 +1016,8 @@ gives the resources **server-httpd** and **ip-httpd** a preference of 100 on
</constraints>
+.. _option_rule:
+
.. index::
pair: cluster option; rule
pair: instance attribute; rule
@@ -820,37 +1031,34 @@ Using Rules to Define Options
Rules may be used to control a variety of options:
-* :ref:`Cluster options <cluster_options>` (``cluster_property_set`` elements)
-* :ref:`Node attributes <node_attributes>` (``instance_attributes`` or
+* :ref:`Cluster options <cluster_options>` (as ``cluster_property_set``
+ elements)
+* :ref:`Node attributes <node_attributes>` (as ``instance_attributes`` or
``utilization`` elements inside a ``node`` element)
-* :ref:`Resource options <resource_options>` (``utilization``,
+* :ref:`Resource options <resource_options>` (as ``utilization``,
``meta_attributes``, or ``instance_attributes`` elements inside a resource
definition element or ``op`` , ``rsc_defaults``, ``op_defaults``, or
``template`` element)
-* :ref:`Operation properties <operation_properties>` (``meta_attributes``
+* :ref:`Operation options <operation_properties>` (as ``meta_attributes``
elements inside an ``op`` or ``op_defaults`` element)
+* :ref:`Alert options <alerts>` (as ``instance_attributes`` or
+ ``meta_attributes`` elements inside an ``alert`` or ``recipient`` element)
-.. note::
-
- Attribute-based expressions for meta-attributes can only be used within
- ``operations`` and ``op_defaults``. They will not work with resource
- configuration or ``rsc_defaults``. Additionally, attribute-based
- expressions cannot be used with cluster options.
Using Rules to Control Resource Options
_______________________________________
Often some cluster nodes will be different from their peers. Sometimes,
-these differences -- e.g. the location of a binary or the names of network
-interfaces -- require resources to be configured differently depending
+these differences (for example, the location of a binary, or the names of
+network interfaces) require resources to be configured differently depending
on the machine they're hosted on.
-By defining multiple ``instance_attributes`` objects for the resource and
+By defining multiple ``instance_attributes`` elements for the resource and
adding a rule to each, we can easily handle these special cases.
In the example below, ``mySpecialRsc`` will use eth1 and port 9999 when run on
-``node1``, eth2 and port 8888 on ``node2`` and default to eth0 and port 9999
-for all other nodes.
+node1, eth2 and port 8888 on node2 and default to eth0 and port 9999 for all
+other nodes.
.. topic:: Defining different resource options based on the node name
@@ -878,20 +1086,20 @@ for all other nodes.
</instance_attributes>
</primitive>
-The order in which ``instance_attributes`` objects are evaluated is determined
-by their score (highest to lowest). If not supplied, the score defaults to
-zero. Objects with an equal score are processed in their listed order. If the
-``instance_attributes`` object has no rule, or a ``rule`` that evaluates to
-``true``, then for any parameter the resource does not yet have a value for,
-the resource will use the parameter values defined by the ``instance_attributes``.
+Multiple ``instance_attributes`` elements are evaluated from highest score to
+lowest. If not supplied, the score defaults to zero. Objects with equal scores
+are processed in their listed order. If an ``instance_attributes`` object has
+no rule or a satisfied ``rule``, then for any parameter the resource does not
+yet have a value for, the resource will use the value defined by the
+``instance_attributes``.
For example, given the configuration above, if the resource is placed on
``node1``:
* ``special-node1`` has the highest score (3) and so is evaluated first; its
- rule evaluates to ``true``, so ``interface`` is set to ``eth1``.
-* ``special-node2`` is evaluated next with score 2, but its rule evaluates to
- ``false``, so it is ignored.
+ rule is satisfied, so ``interface`` is set to ``eth1``.
+* ``special-node2`` is evaluated next with score 2, but its rule is not
+ satisfied, so it is ignored.
* ``defaults`` is evaluated last with score 1, and has no rule, so its values
are examined; ``interface`` is already defined, so the value here is not
used, but ``port`` is not yet defined, so ``port`` is set to ``9999``.
@@ -899,11 +1107,12 @@ For example, given the configuration above, if the resource is placed on
Using Rules to Control Resource Defaults
________________________________________
-Rules can be used for resource and operation defaults. The following example
-illustrates how to set a different ``resource-stickiness`` value during and
-outside work hours. This allows resources to automatically move back to their
-most preferred hosts, but at a time that (in theory) does not interfere with
-business activities.
+Rules can be used for resource and operation defaults.
+
+The following example illustrates how to set a different
+``resource-stickiness`` value during and outside work hours. This allows
+resources to automatically move back to their most preferred hosts, but at a
+time that (in theory) does not interfere with business activities.
.. topic:: Change ``resource-stickiness`` during working hours
@@ -923,20 +1132,8 @@ business activities.
</meta_attributes>
</rsc_defaults>
-Rules may be used similarly in ``instance_attributes`` or ``utilization``
-blocks.
-
-Any single block may directly contain only a single rule, but that rule may
-itself contain any number of rules.
-
-``rsc_expression`` and ``op_expression`` blocks may additionally be used to
-set defaults on either a single resource or across an entire class of resources
-with a single rule. ``rsc_expression`` may be used to select resource agents
-within both ``rsc_defaults`` and ``op_defaults``, while ``op_expression`` may
-only be used within ``op_defaults``. If multiple rules succeed for a given
-resource agent, the last one specified will be the one that takes effect. As
-with any other rule, boolean operations may be used to make more complicated
-expressions.
+``rsc_expression`` is valid within both ``rsc_defaults`` and ``op_defaults``;
+``op_expression`` is valid only within ``op_defaults``.
.. topic:: Default all IPaddr2 resources to stopped
diff --git a/doc/sphinx/Pacemaker_Explained/utilization.rst b/doc/sphinx/Pacemaker_Explained/utilization.rst
index 87eef60..9f3b640 100644
--- a/doc/sphinx/Pacemaker_Explained/utilization.rst
+++ b/doc/sphinx/Pacemaker_Explained/utilization.rst
@@ -3,39 +3,34 @@
Utilization and Placement Strategy
----------------------------------
-Pacemaker decides where to place a resource according to the resource
-assignment scores on every node. The resource will be assigned to the
-node where the resource has the highest score.
+Pacemaker decides where a resource should run by assigning a score to every
+node, considering factors such as the resource's constraints and stickiness,
+then assigning the resource to the node with the highest score.
-If the resource assignment scores on all the nodes are equal, by the default
-placement strategy, Pacemaker will choose a node with the least number of
-assigned resources for balancing the load. If the number of resources on each
-node is equal, the first eligible node listed in the CIB will be chosen to run
-the resource.
+If more than one node has the highest score, Pacemaker by default chooses
+the one with the least number of assigned resources, or if that is also the
+same, the one listed first in the CIB. This results in simple load balancing.
-Often, in real-world situations, different resources use significantly
-different proportions of a node's capacities (memory, I/O, etc.).
-We cannot balance the load ideally just according to the number of resources
-assigned to a node. Besides, if resources are placed such that their combined
-requirements exceed the provided capacity, they may fail to start completely or
-run with degraded performance.
+Sometimes, simple load balancing is insufficient. Different resources can use
+significantly different amounts of a node's memory, CPU, and other capacities.
+Some combinations of resources may strain a node's capacity, causing them to
+fail or have degraded performance. Or, an administrator may prefer to
+concentrate resources rather than balance them, to minimize energy consumption
+by spare nodes.
-To take these factors into account, Pacemaker allows you to configure:
-
-#. The capacity a certain node provides.
-
-#. The capacity a certain resource requires.
-
-#. An overall strategy for placement of resources.
+Pacemaker offers flexibility by allowing you to configure *utilization
+attributes* specifying capacities that each node provides and each resource
+requires, as well as a *placement strategy*.
Utilization attributes
######################
-To configure the capacity that a node provides or a resource requires,
-you can use *utilization attributes* in ``node`` and ``resource`` objects.
-You can name utilization attributes according to your preferences and define as
-many name/value pairs as your configuration needs. However, the attributes'
-values must be integers.
+You can define any number of utilization attributes to represent capacities of
+interest (CPU, memory, I/O bandwidth, etc.). Their values must be integers.
+
+The nature and units of the capacities are irrelevant to Pacemaker. It just
+makes sure that each node has sufficient capacity to run the resources assigned
+to it.
.. topic:: Specifying CPU and RAM capacities of two nodes
@@ -77,16 +72,9 @@ values must be integers.
</utilization>
</primitive>
-A node is considered eligible for a resource if it has sufficient free
-capacity to satisfy the resource's requirements. The nature of the required
-or provided capacities is completely irrelevant to Pacemaker -- it just makes
-sure that all capacity requirements of a resource are satisfied before placing
-a resource to a node.
-
-Utilization attributes used on a node object can also be *transient* *(since 2.1.6)*.
-These attributes are added to a ``transient_attributes`` section for the node
-and are forgotten by the cluster when the node goes offline. The ``attrd_updater``
-tool can be used to set these attributes.
+Utilization attributes for a node may be permanent or *(since 2.1.6)*
+transient. Permanent attributes persist after Pacemaker is restarted, while
+transient attributes do not.
.. topic:: Transient utilization attribute for node cluster-1
@@ -98,98 +86,70 @@ tool can be used to set these attributes.
</utilization>
</transient_attributes>
+Utilization attributes may be configured only on primitive resources. Pacemaker
+will consider a collective resource's utilization based on the primitives it
+contains.
+
.. note::
Utilization is supported for bundles *(since 2.1.3)*, but only for bundles
- with an inner primitive. Any resource utilization values should be specified
- for the inner primitive, but any priority meta-attribute should be specified
- for the outer bundle.
+ with an inner primitive.
Placement Strategy
##################
-After you have configured the capacities your nodes provide and the
-capacities your resources require, you need to set the ``placement-strategy``
-in the global cluster options, otherwise the capacity configurations have
-*no effect*.
+The ``placement-strategy`` cluster option determines how utilization attributes
+are used. Its allowed values are:
-Four values are available for the ``placement-strategy``:
+* ``default``: The cluster ignores utilization values, and places resources
+ according to (from highest to lowest precedence) assignment scores, the
+ number of resources already assigned to each node, and the order nodes are
+ listed in the CIB.
-* **default**
+* ``utilization``: The cluster uses the same method as the default strategy to
+ assign a resource to a node, but only nodes with sufficient free capacity to
+ meet the resource's requirements are eligible.
- Utilization values are not taken into account at all.
- Resources are assigned according to assignment scores. If scores are equal,
- resources are evenly distributed across nodes.
+* ``balanced``: Only nodes with sufficient free capacity are eligible to run a
+ resource, and the cluster load-balances based on the sum of resource
+ utilization values rather than the number of resources.
-* **utilization**
+* ``minimal``: Only nodes with sufficient free capacity are eligible to run a
+ resource, and the cluster concentrates resources on as few nodes as possible.
- Utilization values are taken into account *only* when deciding whether a node
- is considered eligible (i.e. whether it has sufficient free capacity to satisfy
- the resource's requirements). Load-balancing is still done based on the
- number of resources assigned to a node.
-* **balanced**
+To look at it another way, when deciding where to run a resource, the cluster
+starts by considering all nodes, then applies these criteria one by one until
+a single node remains:
- Utilization values are taken into account when deciding whether a node
- is eligible to serve a resource *and* when load-balancing, so an attempt is
- made to spread the resources in a way that optimizes resource performance.
+* If ``placement-strategy`` is ``utilization``, ``balanced``, or ``minimal``,
+ consider only nodes that have sufficient spare capacities to meet the
+ resource's requirements.
-* **minimal**
+* Consider only nodes with the highest score for the resource. Scores take into
+ account factors such as the node's health; the resource's stickiness, failure
+ count on the node, and migration threshold; and constraints.
- Utilization values are taken into account *only* when deciding whether a node
- is eligible to serve a resource. For load-balancing, an attempt is made to
- concentrate the resources on as few nodes as possible, thereby enabling
- possible power savings on the remaining nodes.
-
-Set ``placement-strategy`` with ``crm_attribute``:
-
- .. code-block:: none
-
- # crm_attribute --name placement-strategy --update balanced
-
-Now Pacemaker will ensure the load from your resources will be distributed
-evenly throughout the cluster, without the need for convoluted sets of
-colocation constraints.
-
-Assignment Details
-##################
+* If ``placement-strategy`` is ``balanced``, consider only nodes with the most
+ free capacity.
-Which node is preferred to get consumed first when assigning resources?
-_______________________________________________________________________
+* If ``placement-strategy`` is ``default``, ``utilization``, or ``balanced``,
+ consider only nodes with the least number of assigned resources.
-* The node with the highest node weight gets consumed first. Node weight
- is a score maintained by the cluster to represent node health.
+* If more than one node is eligible after considering all other criteria,
+ choose the one listed first in the CIB.
-* If multiple nodes have the same node weight:
+How Multiple Capacities Combine
+###############################
- * If ``placement-strategy`` is ``default`` or ``utilization``,
- the node that has the least number of assigned resources gets consumed first.
+If only one type of utilization attribute has been defined, free capacity is a
+simple numeric comparison.
- * If their numbers of assigned resources are equal,
- the first eligible node listed in the CIB gets consumed first.
+If multiple utilization attributes have been defined, then the node that has
+the highest value in the most attribute types has the most free capacity.
- * If ``placement-strategy`` is ``balanced``,
- the node that has the most free capacity gets consumed first.
-
- * If the free capacities of the nodes are equal,
- the node that has the least number of assigned resources gets consumed first.
-
- * If their numbers of assigned resources are equal,
- the first eligible node listed in the CIB gets consumed first.
-
- * If ``placement-strategy`` is ``minimal``,
- the first eligible node listed in the CIB gets consumed first.
-
-Which node has more free capacity?
-__________________________________
-
-If only one type of utilization attribute has been defined, free capacity
-is a simple numeric comparison.
-
-If multiple types of utilization attributes have been defined, then
-the node that is numerically highest in the the most attribute types
-has the most free capacity. For example:
+For example:
* If ``nodeA`` has more free ``cpus``, and ``nodeB`` has more free ``memory``,
then their free capacities are equal.
@@ -197,41 +157,46 @@ has the most free capacity. For example:
* If ``nodeA`` has more free ``cpus``, while ``nodeB`` has more free ``memory``
and ``storage``, then ``nodeB`` has more free capacity.
-Which resource is preferred to be assigned first?
-_________________________________________________
+Order of Resource Assignment
+############################
-* The resource that has the highest ``priority`` (see :ref:`resource_options`) gets
- assigned first.
+When assigning resources to nodes, the cluster chooses the next one to assign
+by considering the following criteria one by one until a single resource is
+selected:
-* If their priorities are equal, check whether they are already running. The
- resource that has the highest score on the node where it's running gets assigned
- first, to prevent resource shuffling.
+* Assign the resource with the highest :ref:`priority <meta_priority>`.
-* If the scores above are equal or the resources are not running, the resource has
- the highest score on the preferred node gets assigned first.
+* If any resources are already active, assign the one with the highest score on
+ its current node. This avoids unnecessary resource shuffling.
-* If the scores above are equal, the first runnable resource listed in the CIB
- gets assigned first.
+* Assign the resource with the highest score on its preferred node.
-Limitations and Workarounds
-###########################
+* If more than one resource remains after considering all other criteria,
+ assign the one of them that is listed first in the CIB.
+
+.. note::
+
+ For bundles, only the priority set for the bundle itself matters. If the
+ bundle contains a primitive, the primitive's priority is ignored.
+
+Limitations
+###########
The type of problem Pacemaker is dealing with here is known as the
-`knapsack problem <http://en.wikipedia.org/wiki/Knapsack_problem>`_ and falls into
-the `NP-complete <http://en.wikipedia.org/wiki/NP-complete>`_ category of computer
-science problems -- a fancy way of saying "it takes a really long time
-to solve".
+`knapsack problem <https://en.wikipedia.org/wiki/Knapsack_problem>`_ and falls
+into the `NP-complete <https://en.wikipedia.org/wiki/NP-completeness>`_
+category of computer science problems -- a fancy way of saying "it takes a
+really long time to solve".
-Clearly in a HA cluster, it's not acceptable to spend minutes, let alone hours
-or days, finding an optimal solution while services remain unavailable.
+In a high-availability cluster, it is unacceptable to spend minutes, let alone
+hours or days, finding an optimal solution while services are down.
-So instead of trying to solve the problem completely, Pacemaker uses a
-*best effort* algorithm for determining which node should host a particular
-service. This means it arrives at a solution much faster than traditional
-linear programming algorithms, but by doing so at the price of leaving some
-services stopped.
+Instead of trying to solve the problem completely, Pacemaker uses a "best
+effort" algorithm. This arrives at a quick solution, but at the cost of
+possibly leaving some resources stopped unnecessarily.
-In the contrived example at the start of this chapter:
+Using the example configuration at the start of this chapter, and the balanced
+placement strategy:
* ``rsc-small`` would be assigned to ``node1``
@@ -239,26 +204,23 @@ In the contrived example at the start of this chapter:
* ``rsc-large`` would remain inactive
-Which is not ideal.
-
-There are various approaches to dealing with the limitations of
-pacemaker's placement strategy:
+That is not ideal. There are various approaches to dealing with the limitations
+of Pacemaker's placement strategy:
* **Ensure you have sufficient physical capacity.**
- It might sound obvious, but if the physical capacity of your nodes is (close to)
- maxed out by the cluster under normal conditions, then failover isn't going to
- go well. Even without the utilization feature, you'll start hitting timeouts and
- getting secondary failures.
+ It might sound obvious, but if the physical capacity of your nodes is maxed
+ out even under normal conditions, failover isn't going to go well. Even
+ without the utilization feature, you'll start hitting timeouts and getting
+ secondary failures.
-* **Build some buffer into the capabilities advertised by the nodes.**
+* **Build some buffer into the capacities advertised by the nodes.**
- Advertise slightly more resources than we physically have, on the (usually valid)
- assumption that a resource will not use 100% of the configured amount of
- CPU, memory and so forth *all* the time. This practice is sometimes called *overcommit*.
+ Advertise slightly more resources than we physically have, on the (usually
+ valid) assumption that resources will not always use 100% of their
+ configured utilization. This practice is sometimes called *overcommitting*.
* **Specify resource priorities.**
- If the cluster is going to sacrifice services, it should be the ones you care
- about (comparatively) the least. Ensure that resource priorities are properly set
- so that your most important resources are scheduled first.
+ If the cluster is going to sacrifice services, it should be the ones you
+ care about the least.
diff --git a/doc/sphinx/Pacemaker_Remote/options.rst b/doc/sphinx/Pacemaker_Remote/options.rst
index 4821829..00c56fb 100644
--- a/doc/sphinx/Pacemaker_Remote/options.rst
+++ b/doc/sphinx/Pacemaker_Remote/options.rst
@@ -53,6 +53,10 @@ and define its connection parameters.
+------------------------+-----------------+-----------------------------------------------------------+
| remote-connect-timeout | 60s | How long before a pending guest connection will time out. |
+------------------------+-----------------+-----------------------------------------------------------+
+ | remote-allow-migrate | TRUE | The ``allow-migrate`` meta-attribute value for the |
+ | | | implicit remote connection resource |
+ | | | (``ocf:pacemaker:remote``). |
+ +------------------------+-----------------+-----------------------------------------------------------+
.. index::
pair: configuration; remote node
diff --git a/doc/sphinx/conf.py.in b/doc/sphinx/conf.py.in
index 556eb72..a921b3a 100644
--- a/doc/sphinx/conf.py.in
+++ b/doc/sphinx/conf.py.in
@@ -31,15 +31,17 @@ rst_prolog="""
.. |CFS_DISTRO| replace:: AlmaLinux
.. |CFS_DISTRO_VER| replace:: 9
.. |CRM_BLACKBOX_DIR| replace:: ``%CRM_BLACKBOX_DIR%``
+.. |CRM_CONFIG_DIR| replace:: ``%CRM_CONFIG_DIR%``
.. |CRM_DAEMON_GROUP| replace:: ``%CRM_DAEMON_GROUP%``
.. |CRM_DAEMON_USER| replace:: ``%CRM_DAEMON_USER%``
-.. |CRM_DAEMON_USER_RAW| replace:: %CRM_DAEMON_USER%
.. |CRM_SCHEMA_DIRECTORY| replace:: %CRM_SCHEMA_DIRECTORY%
.. |PCMK_AUTHKEY_FILE| replace:: %PACEMAKER_CONFIG_DIR%/authkey
.. |PCMK_CONFIG_FILE| replace:: ``%CONFIGDIR%/pacemaker``
+.. |PCMK_GNUTLS_PRIORITIES| replace:: %PCMK_GNUTLS_PRIORITIES%
.. |PCMK_INIT_ENV_FILE| replace:: ``%PACEMAKER_CONFIG_DIR%/pcmk-init.env``
.. |PCMK_LOG_FILE| replace:: %CRM_LOG_DIR%/pacemaker.log
-.. |PCMK_GNUTLS_PRIORITIES| replace:: %PCMK_GNUTLS_PRIORITIES%
+.. |PCMK_CONTAINER_LOG_FILE| replace:: ``/var/log/pcmk-init.log``
+.. |PCMK__REMOTE_SCHEMA_DIR| replace:: %PCMK__REMOTE_SCHEMA_DIR%
.. |REMOTE_DISTRO| replace:: AlmaLinux
.. |REMOTE_DISTRO_VER| replace:: 9
"""
diff --git a/etc/sysconfig/pacemaker.in b/etc/sysconfig/pacemaker.in
index 0c3609d..7ec86c0 100644
--- a/etc/sysconfig/pacemaker.in
+++ b/etc/sysconfig/pacemaker.in
@@ -339,6 +339,13 @@
#
# Default: PCMK_schema_directory="@CRM_SCHEMA_DIRECTORY@"
+# PCMK_remote_schema_directory (Advanced Use Only)
+#
+# Specify an alternate location on Pacemaker Remote nodes for storing newer
+# RNG schemas and XSL transforms fetched from the cluster.
+#
+# Default: PCMK_remote_schema_directory="@PCMK__REMOTE_SCHEMA_DIR@"
+
# G_SLICE (Advanced Use Only)
#
# Affect the behavior of glib's memory allocator. Setting to "always-malloc"
@@ -387,7 +394,4 @@
# commands, which would otherwise leave a bunch of unremovable files in /tmp.
#
# Default: VALGRIND_OPTS=""
-VALGRIND_OPTS="--leak-check=full --trace-children=no --vgdb=no --num-callers=25"
-VALGRIND_OPTS="$VALGRIND_OPTS --log-file=@CRM_PACEMAKER_DIR@/valgrind-%p"
-VALGRIND_OPTS="$VALGRIND_OPTS --suppressions=@datadir@/pacemaker/tests/valgrind-pcmk.suppressions"
-VALGRIND_OPTS="$VALGRIND_OPTS --gen-suppressions=all"
+VALGRIND_OPTS="--leak-check=full --trace-children=no --vgdb=no --num-callers=25 --log-file=@CRM_PACEMAKER_DIR@/valgrind-%p --suppressions=@datadir@/pacemaker/tests/valgrind-pcmk.suppressions --gen-suppressions=all"
diff --git a/include/Makefile.am b/include/Makefile.am
index 6618c7a..eb27818 100644
--- a/include/Makefile.am
+++ b/include/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.
#
@@ -13,11 +13,11 @@ MAINTAINERCLEANFILES = Makefile.in \
noinst_HEADERS = config.h \
crm_internal.h \
doxygen.h \
- pacemaker.h \
pacemaker-internal.h \
portability.h \
gettext.h
-pkginclude_HEADERS = crm_config.h
+pkginclude_HEADERS = crm_config.h \
+ pacemaker.h
SUBDIRS = crm pcmki
diff --git a/include/crm/Makefile.am b/include/crm/Makefile.am
index 95564b8..1821579 100644
--- a/include/crm/Makefile.am
+++ b/include/crm/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.
#
@@ -12,11 +12,13 @@ MAINTAINERCLEANFILES = Makefile.in
headerdir=$(pkgincludedir)/crm
header_HEADERS = cib.h \
+ cib_compat.h \
cluster.h \
compatibility.h \
crm.h \
crm_compat.h \
lrmd.h \
+ lrmd_compat.h \
lrmd_events.h \
msg_xml.h \
msg_xml_compat.h \
diff --git a/include/crm/cib.h b/include/crm/cib.h
index a93bfde..fe04d91 100644
--- a/include/crm/cib.h
+++ b/include/crm/cib.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2019 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -26,14 +26,14 @@ extern "C" {
* \ingroup cib
*/
+// Use compare_version() for doing comparisons
# define CIB_FEATURE_SET "2.0"
-/* use compare_version() for doing comparisons */
-
-#define T_CIB_DIFF_NOTIFY "cib_diff_notify"
-
/* Core functions */
+
+// NOTE: sbd (as of at least 1.5.2) uses this
cib_t *cib_new(void);
+
cib_t *cib_native_new(void);
cib_t *cib_file_new(const char *filename);
cib_t *cib_remote_new(const char *server, const char *user, const char *passwd, int port,
@@ -45,6 +45,8 @@ cib_t *cib_shadow_new(const char *name);
void cib_free_notify(cib_t *cib);
void cib_free_callbacks(cib_t *cib);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
void cib_delete(cib_t * cib);
void cib_dump_pending_callbacks(void);
@@ -53,6 +55,10 @@ void remove_cib_op_callback(int call_id, gboolean all_callbacks);
# define CIB_LIBRARY "libcib.so.27"
+#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+#include <crm/cib_compat.h>
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h
index a803311..281c16e 100644
--- a/include/crm/cib/cib_types.h
+++ b/include/crm/cib/cib_types.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -38,14 +38,21 @@ enum cib_variant {
};
enum cib_state {
+ // NOTE: sbd (as of at least 1.5.2) uses this value
cib_connected_command,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
cib_connected_query,
+
cib_disconnected
};
enum cib_conn_type {
cib_command,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
cib_query,
+
cib_no_connection,
cib_command_nonblocking,
};
@@ -78,6 +85,7 @@ enum cib_call_options {
* non-legacy mode.
*/
+ // NOTE: sbd (as of at least 1.5.2) uses this value
//! \deprecated This value will be removed in a future release
cib_scope_local = (1 << 8),
@@ -99,7 +107,31 @@ enum cib_call_options {
*/
cib_transaction = (1 << 10),
+ /*!
+ * \brief Treat new attribute values as atomic score updates where possible
+ *
+ * This option takes effect when updating XML attributes. For an attribute
+ * named \c "name", if the new value is \c "name++" or \c "name+=X" for some
+ * score \c X, the new value is set as follows:
+ * * If attribute \c "name" is not already set to some value in the element
+ * being updated, the new value is set as a literal string.
+ * * If the new value is \c "name++", then the attribute is set to its
+ * existing value (parsed as a score) plus 1.
+ * * If the new value is \c "name+=X" for some score \c X, then the
+ * attribute is set to its existing value plus \c X, where the existing
+ * value and \c X are parsed and added as scores.
+ *
+ * Scores are integer values capped at \c INFINITY and \c -INFINITY. Refer
+ * to Pacemaker Explained and to the \c char2score() function for more
+ * details on scores, including how they're parsed and added.
+ *
+ * Note: This is implemented only for modify operations.
+ */
+ cib_score_update = (1 << 11),
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
cib_sync_call = (1 << 12),
+
cib_no_mtime = (1 << 13),
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
@@ -123,13 +155,16 @@ enum cib_call_options {
typedef struct cib_s cib_t;
typedef struct cib_api_operations_s {
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type);
//! \deprecated This method will be removed and should not be used
int (*signon_raw) (cib_t *cib, const char *name, enum cib_conn_type type,
int *event_fd);
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*signoff) (cib_t *cib);
+
int (*free) (cib_t *cib);
//! \deprecated This method will be removed and should not be used
@@ -137,24 +172,32 @@ typedef struct cib_api_operations_s {
int callid, int rc,
xmlNode *output));
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*add_notify_callback) (cib_t *cib, const char *event,
void (*callback) (const char *event,
xmlNode *msg));
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*del_notify_callback) (cib_t *cib, const char *event,
void (*callback) (const char *event,
xmlNode *msg));
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*set_connection_dnotify) (cib_t *cib,
void (*dnotify) (gpointer user_data));
//! \deprecated This method will be removed and should not be used
int (*inputfd) (cib_t *cib);
+ // NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated This method will be removed and should not be used
int (*noop) (cib_t *cib, int call_options);
int (*ping) (cib_t *cib, xmlNode **output_data, int call_options);
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
int (*query) (cib_t *cib, const char *section, xmlNode **output_data,
int call_options);
+
int (*query_from) (cib_t *cib, const char *host, const char *section,
xmlNode **output_data, int call_options);
@@ -175,6 +218,11 @@ typedef struct cib_api_operations_s {
int call_options);
int (*upgrade) (cib_t *cib, int call_options);
int (*bump_epoch) (cib_t *cib, int call_options);
+
+ /*!
+ * The \c <failed> element in the reply to a failed creation call is
+ * deprecated since 2.1.8.
+ */
int (*create) (cib_t *cib, const char *section, xmlNode *data,
int call_options);
int (*modify) (cib_t *cib, const char *section, xmlNode *data,
@@ -317,17 +365,22 @@ typedef struct cib_api_operations_s {
* \brief Set the user as whom all CIB requests via methods will be executed
*
* By default, the value of the \c CIB_user environment variable is used if
- * set. Otherwise, \c root is used.
+ * set. Otherwise, the current effective user is used.
*
* \param[in,out] cib CIB connection
* \param[in] user Name of user whose permissions to use when
* processing requests
*/
void (*set_user)(cib_t *cib, const char *user);
+
+ int (*fetch_schemas)(cib_t *cib, xmlNode **output_data, const char *after_ver,
+ int call_options);
} cib_api_operations_t;
struct cib_s {
+ // NOTE: sbd (as of at least 1.5.2) uses this
enum cib_state state;
+
enum cib_conn_type type;
enum cib_variant variant;
@@ -342,6 +395,7 @@ struct cib_s {
void (*op_callback) (const xmlNode *msg, int call_id, int rc,
xmlNode *output);
+ // NOTE: sbd (as of at least 1.5.2) uses this
cib_api_operations_t *cmds;
xmlNode *transaction;
diff --git a/include/crm/cib/internal.h b/include/crm/cib/internal.h
index 20059ec..d191009 100644
--- a/include/crm/cib/internal.h
+++ b/include/crm/cib/internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -32,43 +32,7 @@
#define PCMK__CIB_REQUEST_NOOP "noop"
#define PCMK__CIB_REQUEST_SHUTDOWN "cib_shutdown_req"
#define PCMK__CIB_REQUEST_COMMIT_TRANSACT "cib_commit_transact"
-
-# define F_CIB_CLIENTID "cib_clientid"
-# define F_CIB_CALLOPTS "cib_callopt"
-# define F_CIB_CALLID "cib_callid"
-# define F_CIB_CALLDATA "cib_calldata"
-# define F_CIB_OPERATION "cib_op"
-# define F_CIB_ISREPLY "cib_isreplyto"
-# define F_CIB_SECTION "cib_section"
-# define F_CIB_HOST "cib_host"
-# define F_CIB_RC "cib_rc"
-# define F_CIB_UPGRADE_RC "cib_upgrade_rc"
-# define F_CIB_DELEGATED "cib_delegated_from"
-# define F_CIB_OBJID "cib_object"
-# define F_CIB_OBJTYPE "cib_object_type"
-# define F_CIB_EXISTING "cib_existing_object"
-# define F_CIB_SEENCOUNT "cib_seen"
-# define F_CIB_TIMEOUT "cib_timeout"
-# define F_CIB_UPDATE "cib_update"
-# define F_CIB_GLOBAL_UPDATE "cib_update"
-# define F_CIB_UPDATE_RESULT "cib_update_result"
-# define F_CIB_CLIENTNAME "cib_clientname"
-# define F_CIB_NOTIFY_TYPE "cib_notify_type"
-# define F_CIB_NOTIFY_ACTIVATE "cib_notify_activate"
-# define F_CIB_UPDATE_DIFF "cib_update_diff"
-# define F_CIB_USER "cib_user"
-# define F_CIB_LOCAL_NOTIFY_ID "cib_local_notify_id"
-# define F_CIB_PING_ID "cib_ping_id"
-# define F_CIB_SCHEMA_MAX "cib_schema_max"
-
-# define T_CIB "cib"
-# define T_CIB_COMMAND "cib_command"
-# define T_CIB_NOTIFY "cib_notify"
-/* notify sub-types */
-# define T_CIB_PRE_NOTIFY "cib_pre_notify"
-# define T_CIB_POST_NOTIFY "cib_post_notify"
-# define T_CIB_TRANSACTION "cib_transaction"
-# define T_CIB_UPDATE_CONFIRM "cib_update_confirmation"
+#define PCMK__CIB_REQUEST_SCHEMAS "cib_schemas"
/*!
* \internal
@@ -110,6 +74,7 @@ enum cib__op_type {
cib__op_sync_all,
cib__op_sync_one,
cib__op_upgrade,
+ cib__op_schemas,
};
gboolean cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
@@ -204,7 +169,7 @@ int cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset);
bool cib__element_in_patchset(const xmlNode *patchset, const char *element);
-int cib_perform_op(const char *op, int call_options, cib__op_fn_t fn,
+int 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,
diff --git a/include/crm/cib/util.h b/include/crm/cib/util.h
index 18726bb..b68c061 100644
--- a/include/crm/cib/util.h
+++ b/include/crm/cib/util.h
@@ -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.
*
@@ -50,15 +50,7 @@ int delete_attr_delegate(cib_t * the_cib, int options,
int query_node_uuid(cib_t * the_cib, const char *uname, char **uuid, int *is_remote_node);
-int query_node_uname(cib_t * the_cib, const char *uuid, char **uname);
-
-int set_standby(cib_t * the_cib, const char *uuid, const char *scope, const char *standby_value);
-
-xmlNode *cib_get_generation(cib_t * cib);
-
-void cib_metadata(void);
-const char *cib_pref(GHashTable * options, const char *name);
-
+// NOTE: sbd (as of at least 1.5.2) uses this
int cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
int level);
diff --git a/include/crm/cib/util_compat.h b/include/crm/cib/util_compat.h
index 20f1e2d..99b2551 100644
--- a/include/crm/cib/util_compat.h
+++ b/include/crm/cib/util_compat.h
@@ -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.
*
@@ -10,6 +10,10 @@
#ifndef PCMK__CRM_CIB_UTIL_COMPAT__H
# define PCMK__CRM_CIB_UTIL_COMPAT__H
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/cib/cib_types.h> // cib_t
+
#include <crm/common/xml.h>
#ifdef __cplusplus
extern "C" {
@@ -33,6 +37,22 @@ const char *get_object_parent(const char *object_type);
//! \deprecated Use pcmk_cib_xpath_for() instead
xmlNode *get_object_root(const char *object_type, xmlNode *the_root);
+//! \deprecated Do not use
+int set_standby(cib_t *the_cib, const char *uuid, const char *scope,
+ const char *standby_value);
+
+//! \deprecated Do not use
+int query_node_uname(cib_t * the_cib, const char *uuid, char **uname);
+
+//! \deprecated Do not use
+xmlNode *cib_get_generation(cib_t *cib);
+
+//! \deprecated Do not use
+const char *cib_pref(GHashTable * options, const char *name);
+
+//! \deprecated Do not use
+void cib_metadata(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/cib_compat.h b/include/crm/cib_compat.h
new file mode 100644
index 0000000..01f1d6c
--- /dev/null
+++ b/include/crm/cib_compat.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_CIB_COMPAT__H
+# define PCMK__CRM_CIB_COMPAT__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Deprecated CIB utilities
+ * \ingroup core
+ * \deprecated Do not include this header directly. The utilities in this
+ * header, and the header itself, will be removed in a future
+ * release.
+ */
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use
+#define T_CIB_DIFF_NOTIFY "cib_diff_notify"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_CIB_COMPAT__H
diff --git a/include/crm/cluster.h b/include/crm/cluster.h
index b61fd70..778c4ba 100644
--- a/include/crm/cluster.h
+++ b/include/crm/cluster.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -24,14 +24,33 @@ extern "C" {
# include <corosync/cpg.h>
# endif
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
extern gboolean crm_have_quorum;
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
extern GHashTable *crm_peer_cache;
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
extern GHashTable *crm_remote_peer_cache;
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
extern unsigned long long crm_peer_seq;
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
#define CRM_NODE_LOST "lost"
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
#define CRM_NODE_MEMBER "member"
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum crm_join_phase {
/* @COMPAT: crm_join_nack_quiet can be replaced by crm_node_t:user_data
* at a compatibility break.
@@ -46,18 +65,47 @@ enum crm_join_phase {
crm_join_finalized = 3,
crm_join_confirmed = 4,
};
+//!@}
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum crm_node_flags {
- /* node is not a cluster node and should not be considered for cluster membership */
- crm_remote_node = 0x0001,
+ /* Node is not a cluster node and should not be considered for cluster
+ * membership
+ */
+ crm_remote_node = (1U << 0),
- /* node's cache entry is dirty */
- crm_node_dirty = 0x0010,
+ // Node's cache entry is dirty
+ crm_node_dirty = (1U << 1),
};
+//!@}
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
typedef struct crm_peer_node_s {
char *uname; // Node name as known to cluster
- char *uuid; // Node UUID to ensure uniqueness
+
+ /* @COMPAT This is less than ideal since the value is not a valid XML ID
+ * (for Corosync, it's the string equivalent of the node's numeric node ID,
+ * but XML IDs can't start with a number) and the three elements should have
+ * different IDs.
+ *
+ * Ideally, we would use something like node-NODEID, node_state-NODEID, and
+ * transient_attributes-NODEID as the element IDs. Unfortunately changing it
+ * would be impractical due to backward compatibility; older nodes in a
+ * rolling upgrade will always write and expect the value in the old format.
+ *
+ * This is also named poorly, since the value is not a UUID, but at least
+ * that can be changed at an API compatibility break.
+ */
+ /*! Value of the PCMK_XA_ID XML attribute to use with the node's
+ * PCMK_XE_NODE, PCMK_XE_NODE_STATE, and PCMK_XE_TRANSIENT_ATTRIBUTES
+ * XML elements in the CIB
+ */
+ char *uuid;
+
char *state; // @TODO change to enum
uint64_t flags; // Bitmask of crm_node_flags
uint64_t last_seen; // Only needed by cluster nodes
@@ -82,15 +130,19 @@ typedef struct crm_peer_node_s {
time_t when_member; // Since when node has been a cluster member
time_t when_online; // Since when peer has been online in CPG
} crm_node_t;
+//!@}
-void crm_peer_init(void);
-void crm_peer_destroy(void);
-
-typedef struct crm_cluster_s {
+// Implementation of pcmk_cluster_t
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
+struct crm_cluster_s {
char *uuid;
char *uname;
uint32_t nodeid;
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_cluster_set_destroy_fn() to set this
void (*destroy) (gpointer);
# if SUPPORT_COROSYNC
@@ -99,119 +151,94 @@ typedef struct crm_cluster_s {
* cluster layer further.
*/
struct cpg_name group;
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ /*!
+ * \deprecated Call pcmk_cpg_set_deliver_fn() and pcmk_cpg_set_confchg_fn()
+ * to set these
+ */
cpg_callbacks_t cpg;
+
cpg_handle_t cpg_handle;
# endif
-} crm_cluster_t;
+};
+//!@}
+
+//! Connection to a cluster layer
+typedef struct crm_cluster_s pcmk_cluster_t;
-gboolean crm_cluster_connect(crm_cluster_t *cluster);
-void crm_cluster_disconnect(crm_cluster_t *cluster);
+int pcmk_cluster_connect(pcmk_cluster_t *cluster);
+int pcmk_cluster_disconnect(pcmk_cluster_t *cluster);
-crm_cluster_t *pcmk_cluster_new(void);
-void pcmk_cluster_free(crm_cluster_t *cluster);
+pcmk_cluster_t *pcmk_cluster_new(void);
+void pcmk_cluster_free(pcmk_cluster_t *cluster);
+int pcmk_cluster_set_destroy_fn(pcmk_cluster_t *cluster, void (*fn)(gpointer));
+#if SUPPORT_COROSYNC
+int pcmk_cpg_set_deliver_fn(pcmk_cluster_t *cluster, cpg_deliver_fn_t fn);
+int pcmk_cpg_set_confchg_fn(pcmk_cluster_t *cluster, cpg_confchg_fn_t fn);
+#endif // SUPPORT_COROSYNC
+
+/* @COMPAT Make this internal when we can break API backward compatibility. Also
+ * evaluate whether we can drop this entirely. Since 2.0.0, we have sent only
+ * messages with crm_class_cluster.
+ */
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum crm_ais_msg_class {
crm_class_cluster = 0,
};
+//!@}
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum crm_ais_msg_types {
crm_msg_none = 0,
- crm_msg_ais = 1,
+ crm_msg_ais = 1, // Unused
crm_msg_lrmd = 2,
crm_msg_cib = 3,
crm_msg_crmd = 4,
crm_msg_attrd = 5,
- crm_msg_stonithd = 6,
- crm_msg_te = 7,
- crm_msg_pe = 8,
+ crm_msg_stonithd = 6, // Unused
+ crm_msg_te = 7, // Unused
+ crm_msg_pe = 8, // Unused
crm_msg_stonith_ng = 9,
};
+//!@}
-/* used with crm_get_peer_full */
-enum crm_get_peer_flags {
- CRM_GET_PEER_CLUSTER = 0x0001,
- CRM_GET_PEER_REMOTE = 0x0002,
- CRM_GET_PEER_ANY = CRM_GET_PEER_CLUSTER|CRM_GET_PEER_REMOTE,
-};
-
-gboolean send_cluster_message(const crm_node_t *node,
- enum crm_ais_msg_types service,
- const xmlNode *data, gboolean ordered);
-
-int crm_remote_peer_cache_size(void);
-
-/* Initialize and refresh the remote peer cache from a cib config */
-void crm_remote_peer_cache_refresh(xmlNode *cib);
-crm_node_t *crm_remote_peer_get(const char *node_name);
-void crm_remote_peer_cache_remove(const char *node_name);
-
-/* allows filtering of remote and cluster nodes using crm_get_peer_flags */
-crm_node_t *crm_get_peer_full(unsigned int id, const char *uname, int flags);
-
-/* only searches cluster nodes */
-crm_node_t *crm_get_peer(unsigned int id, const char *uname);
-
-guint crm_active_peers(void);
-gboolean crm_is_peer_active(const crm_node_t * node);
-guint reap_crm_member(uint32_t id, const char *name);
-
-# if SUPPORT_COROSYNC
-uint32_t get_local_nodeid(cpg_handle_t handle);
-
-gboolean cluster_connect_cpg(crm_cluster_t *cluster);
-void cluster_disconnect_cpg(crm_cluster_t * cluster);
-
-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);
-gboolean crm_is_corosync_peer_active(const crm_node_t * node);
-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);
-char *pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid, void *msg,
- uint32_t *kind, const char **from);
-# endif
-
-const char *crm_peer_uuid(crm_node_t *node);
-const char *crm_peer_uname(const char *uuid);
-
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum crm_status_type {
crm_status_uname,
crm_status_nstate,
crm_status_processes,
};
+//!@}
-enum crm_ais_msg_types text2msg_type(const char *text);
-void crm_set_status_callback(void (*dispatch) (enum crm_status_type, crm_node_t *, const void *));
-void crm_set_autoreap(gboolean autoreap);
-
-enum cluster_type_e {
- pcmk_cluster_unknown = 0x0001,
- pcmk_cluster_invalid = 0x0002,
- // 0x0004 was heartbeat
- // 0x0010 was corosync 1 with plugin
- pcmk_cluster_corosync = 0x0020,
- // 0x0040 was corosync 1 with CMAN
+/*!
+ * \enum pcmk_cluster_layer
+ * \brief Types of cluster layer
+ */
+enum pcmk_cluster_layer {
+ pcmk_cluster_layer_unknown = 1, //!< Unknown cluster layer
+ pcmk_cluster_layer_invalid = 2, //!< Invalid cluster layer
+ pcmk_cluster_layer_corosync = 32, //!< Corosync Cluster Engine
};
-enum cluster_type_e get_cluster_type(void);
-const char *name_for_cluster_type(enum cluster_type_e type);
+enum pcmk_cluster_layer pcmk_get_cluster_layer(void);
+const char *pcmk_cluster_layer_text(enum pcmk_cluster_layer layer);
-gboolean is_corosync_cluster(void);
-
-const char *get_local_node_name(void);
-char *get_node_name(uint32_t nodeid);
-
-/*!
+/*
* \brief Get log-friendly string equivalent of a join phase
*
* \param[in] phase Join phase
*
* \return Log-friendly string equivalent of \p phase
*/
+//! \deprecated Do not use (public access will be removed in a future release)
static inline const char *
crm_join_phase_str(enum crm_join_phase phase)
{
diff --git a/include/crm/cluster/compat.h b/include/crm/cluster/compat.h
index 89a03fd..6802edf 100644
--- a/include/crm/cluster/compat.h
+++ b/include/crm/cluster/compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,7 +10,16 @@
#ifndef PCMK__CRM_CLUSTER_COMPAT__H
# define PCMK__CRM_CLUSTER_COMPAT__H
+#include <stdint.h> // uint32_t
+#include <sys/types.h> // size_t
+
+#include <glib.h> // gboolean, guint
#include <libxml/tree.h> // xmlNode
+
+#if SUPPORT_COROSYNC
+#include <corosync/cpg.h> // cpg_handle_t
+#endif // SUPPORT_COROSYNC
+
#include <crm/cluster.h> // crm_node_t
#ifdef __cplusplus
@@ -26,16 +35,158 @@ extern "C" {
* release.
*/
-// \deprecated Use stonith_api_kick() from libstonithd instead
+//! \deprecated Do not use
+enum crm_get_peer_flags {
+ CRM_GET_PEER_CLUSTER = 0x0001,
+ CRM_GET_PEER_REMOTE = 0x0002,
+ CRM_GET_PEER_ANY = CRM_GET_PEER_CLUSTER|CRM_GET_PEER_REMOTE,
+};
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use \c pcmk_cluster_t instead
+typedef pcmk_cluster_t crm_cluster_t;
+
+//! \deprecated Do not use Pacemaker for cluster node cacheing
+crm_node_t *crm_get_peer(unsigned int id, const char *uname);
+
+//! \deprecated Do not use Pacemaker for cluster node cacheing
+crm_node_t *crm_get_peer_full(unsigned int id, const char *uname, int flags);
+
+//! \deprecated Use stonith_api_kick() from libstonithd instead
int crm_terminate_member(int nodeid, const char *uname, void *unused);
-// \deprecated Use stonith_api_kick() from libstonithd instead
+//! \deprecated Use \c stonith_api_kick() from libstonithd instead
int crm_terminate_member_no_mainloop(int nodeid, const char *uname,
int *connection);
-// \deprecated Use crm_xml_add(xml, attr, crm_peer_uuid(node)) instead
+/*!
+ * \deprecated Use
+ * <tt>crm_xml_add(xml, attr, pcmk__cluster_node_uuid(node))</tt>
+ * instead
+ */
void set_uuid(xmlNode *xml, const char *attr, crm_node_t *node);
+#if SUPPORT_COROSYNC
+
+//! \deprecated Do not use
+gboolean cluster_connect_cpg(pcmk_cluster_t *cluster);
+
+//! \deprecated Do not use
+void cluster_disconnect_cpg(pcmk_cluster_t *cluster);
+
+//! \deprecated Do not use
+uint32_t get_local_nodeid(cpg_handle_t handle);
+
+//! \deprecated Do not use
+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);
+
+//! \deprecated Do not use
+gboolean crm_is_corosync_peer_active(const crm_node_t * node);
+
+//! \deprecated Do not use
+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);
+
+//! \deprecated Do not use
+char *pcmk_message_common_cs(cpg_handle_t handle, uint32_t nodeid, uint32_t pid,
+ void *msg, uint32_t *kind, const char **from);
+
+#endif // SUPPORT_COROSYNC
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use \c pcmk_cluster_connect() instead
+gboolean crm_cluster_connect(pcmk_cluster_t *cluster);
+
+//! \deprecated Use \c pcmk_cluster_disconnect() instead
+void crm_cluster_disconnect(pcmk_cluster_t *cluster);
+
+//! \deprecated Do not use
+int crm_remote_peer_cache_size(void);
+
+//! \deprecated Do not use
+void crm_remote_peer_cache_refresh(xmlNode *cib);
+
+//! \deprecated Do not use
+crm_node_t *crm_remote_peer_get(const char *node_name);
+
+//! \deprecated Do not use
+void crm_remote_peer_cache_remove(const char *node_name);
+
+//! \deprecated Do not use
+gboolean crm_is_peer_active(const crm_node_t *node);
+
+//! \deprecated Do not use
+guint crm_active_peers(void);
+
+//! \deprecated Do not use
+guint reap_crm_member(uint32_t id, const char *name);
+
+// NOTE: sbd (as of at least 1.5.2) uses this enum
+//!@{
+//! \deprecated Use <tt>enum pcmk_cluster_layer</tt> instead
+enum cluster_type_e {
+ // NOTE: sbd (as of at least 1.5.2) uses this value
+ pcmk_cluster_unknown = pcmk_cluster_layer_unknown,
+
+ pcmk_cluster_invalid = pcmk_cluster_layer_invalid,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
+ pcmk_cluster_corosync = pcmk_cluster_layer_corosync,
+};
+//!@}
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use \c pcmk_cluster_layer_text() instead
+const char *name_for_cluster_type(enum cluster_type_e type);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use \c pcmk_get_cluster_layer() instead
+enum cluster_type_e get_cluster_type(void);
+
+//! \deprecated Use \c pcmk_get_cluster_layer() instead
+gboolean is_corosync_cluster(void);
+
+//! \deprecated Do not use
+void crm_peer_init(void);
+
+//! \deprecated Do not use
+void crm_peer_destroy(void);
+
+//! \deprecated Do not use
+gboolean send_cluster_message(const crm_node_t *node,
+ enum crm_ais_msg_types service,
+ const xmlNode *data, gboolean ordered);
+
+//! \deprecated Do not use
+const char *crm_peer_uuid(crm_node_t *node);
+
+//! \deprecated Do not use
+enum crm_ais_msg_types text2msg_type(const char *text);
+
+//! \deprecated Do not use
+char *get_node_name(uint32_t nodeid);
+
+//! \deprecated Do not use
+const char *get_local_node_name(void);
+
+//! \deprecated Do not use
+void crm_set_autoreap(gboolean enable);
+
+//! \deprecated Do not use
+void crm_set_status_callback(void (*dispatch)(enum crm_status_type,
+ crm_node_t *, const void *));
+
+//! \deprecated Do not use
+const char *crm_peer_uname(const char *uuid);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/cluster/internal.h b/include/crm/cluster/internal.h
index e20ee4c..fc24c77 100644
--- a/include/crm/cluster/internal.h
+++ b/include/crm/cluster/internal.h
@@ -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.
*
@@ -7,28 +7,49 @@
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
-#ifndef CRM_CLUSTER_INTERNAL__H
-# define CRM_CLUSTER_INTERNAL__H
+#ifndef PCMK__CRM_CLUSTER_INTERNAL__H
+# define PCMK__CRM_CLUSTER_INTERNAL__H
+# include <stdbool.h>
# include <stdint.h> // uint32_t, uint64_t
+
+# include <glib.h> // gboolean
+
# include <crm/cluster.h>
-/* *INDENT-OFF* */
enum crm_proc_flag {
+ /* @COMPAT When crm_node_t:processes is made internal, we can merge this
+ * into node flags or turn it into a boolean. Until then, in theory
+ * something could depend on these particular numeric values.
+ */
crm_proc_none = 0x00000001,
// Cluster layers
crm_proc_cpg = 0x04000000,
+};
+
+// Used with node cache search functions
+enum pcmk__node_search_flags {
+ //! Does not affect search
+ pcmk__node_search_none = 0,
+
+ //! Search for cluster nodes from membership cache
+ pcmk__node_search_cluster_member = (1 << 0),
- // Daemons
- crm_proc_execd = 0x00000010,
- crm_proc_based = 0x00000100,
- crm_proc_controld = 0x00000200,
- crm_proc_attrd = 0x00001000,
- crm_proc_schedulerd = 0x00010000,
- crm_proc_fenced = 0x00100000,
+ //! Search for remote nodes
+ pcmk__node_search_remote = (1 << 1),
+
+ //! Search for cluster member nodes and remote nodes
+ pcmk__node_search_any = pcmk__node_search_cluster_member
+ |pcmk__node_search_remote,
+
+ /* @COMPAT The values before this must stay the same until we can drop
+ * support for enum crm_get_peer_flags
+ */
+
+ //! Search for cluster nodes from CIB (as of last cache refresh)
+ pcmk__node_search_cluster_cib = (1 << 2),
};
-/* *INDENT-ON* */
/*!
* \internal
@@ -39,8 +60,8 @@ enum crm_proc_flag {
static inline uint32_t
crm_get_cluster_proc(void)
{
- switch (get_cluster_type()) {
- case pcmk_cluster_corosync:
+ switch (pcmk_get_cluster_layer()) {
+ case pcmk_cluster_layer_corosync:
return crm_proc_cpg;
default:
@@ -108,8 +129,27 @@ pcmk__cs_err_str(int error)
char *pcmk__corosync_cluster_name(void);
bool pcmk__corosync_add_nodes(xmlNode *xml_parent);
+
+void 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);
+
+char *pcmk__cpg_message_data(cpg_handle_t handle, uint32_t sender_id,
+ uint32_t pid, void *content, uint32_t *kind,
+ const char **from);
+
# endif
+const char *pcmk__cluster_node_uuid(crm_node_t *node);
+char *pcmk__cluster_node_name(uint32_t nodeid);
+const char *pcmk__cluster_local_node_name(void);
+const char *pcmk__node_name_from_uuid(const char *uuid);
+
crm_node_t *crm_update_peer_proc(const char *source, crm_node_t * peer,
uint32_t flag, const char *status);
crm_node_t *pcmk__update_peer_state(const char *source, crm_node_t *node,
@@ -122,18 +162,36 @@ void pcmk__reap_unseen_nodes(uint64_t ring_id);
void pcmk__corosync_quorum_connect(gboolean (*dispatch)(unsigned long long,
gboolean),
void (*destroy) (gpointer));
+
+enum crm_ais_msg_types pcmk__cluster_parse_msg_type(const char *text);
+bool pcmk__cluster_send_message(const crm_node_t *node,
+ enum crm_ais_msg_types service,
+ const xmlNode *data);
+
+// Membership
+
+void pcmk__cluster_init_node_caches(void);
+void pcmk__cluster_destroy_node_caches(void);
+
+void pcmk__cluster_set_autoreap(bool enable);
+void pcmk__cluster_set_status_callback(void (*dispatch)(enum crm_status_type,
+ crm_node_t *,
+ const void *));
+
+bool pcmk__cluster_is_node_active(const crm_node_t *node);
+unsigned int pcmk__cluster_num_active_nodes(void);
+unsigned int pcmk__cluster_num_remote_nodes(void);
+
+crm_node_t *pcmk__cluster_lookup_remote_node(const char *node_name);
+void pcmk__cluster_forget_cluster_node(uint32_t id, const char *node_name);
+void pcmk__cluster_forget_remote_node(const char *node_name);
crm_node_t *pcmk__search_node_caches(unsigned int id, const char *uname,
uint32_t flags);
-crm_node_t *pcmk__search_cluster_node_cache(unsigned int id, const char *uname,
- const char *uuid);
+void pcmk__purge_node_from_cache(const char *node_name, uint32_t node_id);
void pcmk__refresh_node_caches_from_cib(xmlNode *cib);
-crm_node_t *pcmk__search_known_node_cache(unsigned int id, const char *uname,
- uint32_t flags);
-crm_node_t *pcmk__get_peer(unsigned int id, const char *uname,
- const char *uuid);
-crm_node_t *pcmk__get_peer_full(unsigned int id, const char *uname,
- const char *uuid, int flags);
+crm_node_t *pcmk__get_node(unsigned int id, const char *uname,
+ const char *uuid, uint32_t flags);
-#endif
+#endif // PCMK__CRM_CLUSTER_INTERNAL__H
diff --git a/include/crm/common/Makefile.am b/include/crm/common/Makefile.am
index 83a4197..4d2055f 100644
--- a/include/crm/common/Makefile.am
+++ b/include/crm/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.
#
@@ -27,18 +27,26 @@ header_HEADERS = acl.h \
mainloop_compat.h \
nodes.h \
nvpair.h \
+ options.h \
output.h \
resources.h \
results.h \
results_compat.h \
roles.h \
+ rules.h \
scheduler.h \
scheduler_types.h \
+ schemas.h \
+ scores.h \
+ scores_compat.h \
tags.h \
tickets.h \
util.h \
util_compat.h \
xml.h \
- xml_compat.h
+ xml_compat.h \
+ xml_io.h \
+ xml_io_compat.h \
+ xml_names.h
noinst_HEADERS = $(wildcard *internal.h)
diff --git a/include/crm/common/acl.h b/include/crm/common/acl.h
index 655ad55..f8f4b68 100644
--- a/include/crm/common/acl.h
+++ b/include/crm/common/acl.h
@@ -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.
*
@@ -8,10 +8,10 @@
*/
#ifndef PCMK__CRM_COMMON_ACL__H
-# define PCMK__CRM_COMMON_ACL__H
+#define PCMK__CRM_COMMON_ACL__H
-# include <libxml/tree.h> // xmlNode
-# include <stdbool.h>
+#include <libxml/tree.h> // xmlNode
+#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/acl_internal.h b/include/crm/common/acl_internal.h
index ca67d68..8512407 100644
--- a/include/crm/common/acl_internal.h
+++ b/include/crm/common/acl_internal.h
@@ -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.
*
@@ -10,7 +10,10 @@
#ifndef CRM_COMMON_ACL_INTERNAL__H
#define CRM_COMMON_ACL_INTERNAL__H
-#include <string.h> // strcmp()
+#include <string.h> // strcmp()
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/common/xml_internal.h> // enum xml_private_flags
/* internal ACL-related utilities */
diff --git a/include/crm/common/action_relation_internal.h b/include/crm/common/action_relation_internal.h
index e789131..1401f9d 100644
--- a/include/crm/common/action_relation_internal.h
+++ b/include/crm/common/action_relation_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 the Pacemaker project contributors
+ * Copyright 2023-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,10 @@
*/
#ifndef PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H
-# define PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H
+#define PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H
+
+#include <stdint.h> // uint32_t
+#include <crm/common/scheduler_types.h> // pcmk_resource_t, pcmk_action_t
/*!
* Flags to indicate the relationship between two actions
@@ -127,6 +130,50 @@ enum pcmk__action_relation_flags {
pcmk__ar_then_cancels_first = (1U << 25),
};
+/* Action relation object
+ *
+ * The most common type of relation is an ordering, in which case action1 etc.
+ * refers to the "first" action, and action2 etc. refers to the "then" action.
+ */
+typedef struct {
+ int id; // Counter to identify relation
+ uint32_t flags; // Group of enum pcmk__action_relation_flags
+ pcmk_resource_t *rsc1; // Resource for first action, if any
+ pcmk_action_t *action1; // First action in relation
+ char *task1; // Action name or key for first action
+ pcmk_resource_t *rsc2; // Resource for second action, if any
+ pcmk_action_t *action2; // Second action in relation
+ char *task2; // Action name or key for second action
+} pcmk__action_relation_t;
+
typedef struct pe_action_wrapper_s pcmk__related_action_t;
+/*!
+ * \internal
+ * \brief Set action relation flags
+ *
+ * \param[in,out] ar_flags Flag group to modify
+ * \param[in] flags_to_set enum pcmk__action_relation_flags to set
+ */
+#define pcmk__set_relation_flags(ar_flags, flags_to_set) do { \
+ ar_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
+ "Action relation", "constraint", \
+ ar_flags, (flags_to_set), \
+ #flags_to_set); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Clear action relation flags
+ *
+ * \param[in,out] ar_flags Flag group to modify
+ * \param[in] flags_to_clear enum pcmk__action_relation_flags to clear
+ */
+#define pcmk__clear_relation_flags(ar_flags, flags_to_clear) do { \
+ ar_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \
+ "Action relation", "constraint", \
+ ar_flags, (flags_to_clear), \
+ #flags_to_clear); \
+ } while (0)
+
#endif // PCMK__CRM_COMMON_ACTION_RELATION_INTERNAL__H
diff --git a/include/crm/common/actions.h b/include/crm/common/actions.h
index 5d2784d..899aa8b 100644
--- a/include/crm/common/actions.h
+++ b/include/crm/common/actions.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -54,6 +54,7 @@ extern "C" {
#define PCMK_ACTION_LOAD_STOPPED "load_stopped"
#define PCMK_ACTION_MAINTENANCE_NODES "maintenance_nodes"
#define PCMK_ACTION_META_DATA "meta-data"
+#define PCMK_ACTION_METADATA "metadata"
#define PCMK_ACTION_MIGRATE_FROM "migrate_from"
#define PCMK_ACTION_MIGRATE_TO "migrate_to"
#define PCMK_ACTION_MONITOR "monitor"
@@ -75,77 +76,57 @@ extern "C" {
#define PCMK_ACTION_STOPPED "stopped"
#define PCMK_ACTION_VALIDATE_ALL "validate-all"
-//! Possible actions (including some pseudo-actions)
+// Possible actions (including some pseudo-actions)
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum action_tasks {
- pcmk_action_unspecified = 0, //!< Unspecified or unknown action
- pcmk_action_monitor, //!< Monitor
+ pcmk_action_unspecified = 0, // Unspecified or unknown action
+ pcmk_action_monitor, // Monitor
// Each "completed" action must be the regular action plus 1
- pcmk_action_stop, //!< Stop
- pcmk_action_stopped, //!< Stop completed
+ pcmk_action_stop, // Stop
+ pcmk_action_stopped, // Stop completed
- pcmk_action_start, //!< Start
- pcmk_action_started, //!< Start completed
+ pcmk_action_start, // Start
+ pcmk_action_started, // Start completed
- pcmk_action_notify, //!< Notify
- pcmk_action_notified, //!< Notify completed
+ pcmk_action_notify, // Notify
+ pcmk_action_notified, // Notify completed
- pcmk_action_promote, //!< Promote
- pcmk_action_promoted, //!< Promoted
+ pcmk_action_promote, // Promote
+ pcmk_action_promoted, // Promoted
- pcmk_action_demote, //!< Demote
- pcmk_action_demoted, //!< Demoted
+ pcmk_action_demote, // Demote
+ pcmk_action_demoted, // Demoted
- pcmk_action_shutdown, //!< Shut down node
- pcmk_action_fence, //!< Fence node
+ pcmk_action_shutdown, // Shut down node
+ pcmk_action_fence, // Fence node
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_action_unspecified instead
no_action = pcmk_action_unspecified,
-
- //! \deprecated Use pcmk_action_monitor instead
monitor_rsc = pcmk_action_monitor,
-
- //! \deprecated Use pcmk_action_stop instead
stop_rsc = pcmk_action_stop,
-
- //! \deprecated Use pcmk_action_stopped instead
stopped_rsc = pcmk_action_stopped,
-
- //! \deprecated Use pcmk_action_start instead
start_rsc = pcmk_action_start,
-
- //! \deprecated Use pcmk_action_started instead
started_rsc = pcmk_action_started,
-
- //! \deprecated Use pcmk_action_notify instead
action_notify = pcmk_action_notify,
-
- //! \deprecated Use pcmk_action_notified instead
action_notified = pcmk_action_notified,
-
- //! \deprecated Use pcmk_action_promote instead
action_promote = pcmk_action_promote,
-
- //! \deprecated Use pcmk_action_promoted instead
action_promoted = pcmk_action_promoted,
-
- //! \deprecated Use pcmk_action_demote instead
action_demote = pcmk_action_demote,
-
- //! \deprecated Use pcmk_action_demoted instead
action_demoted = pcmk_action_demoted,
-
- //! \deprecated Use pcmk_action_shutdown instead
shutdown_crm = pcmk_action_shutdown,
-
- //! \deprecated Use pcmk_action_fence instead
stonith_node = pcmk_action_fence,
#endif
};
+//!@}
-//! Possible responses to a resource action failure
+// Possible responses to a resource action failure
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum action_fail_response {
/* The order is (partially) significant here; the values from
* pcmk_on_fail_ignore through pcmk_on_fail_fence_node are in order of
@@ -160,33 +141,33 @@ enum action_fail_response {
*/
// @TODO Define as 10
- pcmk_on_fail_ignore = 0, //!< Act as if failure didn't happen
+ pcmk_on_fail_ignore = 0, // Act as if failure didn't happen
// @TODO Define as 30
- pcmk_on_fail_restart = 1, //!< Restart resource
+ pcmk_on_fail_restart = 1, // Restart resource
// @TODO Define as 60
- pcmk_on_fail_ban = 2, //!< Ban resource from current node
+ pcmk_on_fail_ban = 2, // Ban resource from current node
// @TODO Define as 70
- pcmk_on_fail_block = 3, //!< Treat resource as unmanaged
+ pcmk_on_fail_block = 3, // Treat resource as unmanaged
// @TODO Define as 80
- pcmk_on_fail_stop = 4, //!< Stop resource and leave stopped
+ pcmk_on_fail_stop = 4, // Stop resource and leave stopped
// @TODO Define as 90
- pcmk_on_fail_standby_node = 5, //!< Put resource's node in standby
+ pcmk_on_fail_standby_node = 5, // Put resource's node in standby
// @TODO Define as 100
- pcmk_on_fail_fence_node = 6, //!< Fence resource's node
+ pcmk_on_fail_fence_node = 6, // Fence resource's node
// @COMPAT Values below here are out of desired order for API compatibility
// @TODO Define as 50
- pcmk_on_fail_restart_container = 7, //!< Restart resource's container
+ pcmk_on_fail_restart_container = 7, // Restart resource's container
// @TODO Define as 40
- /*!
+ /*
* Fence the remote node created by the resource if fencing is enabled,
* otherwise attempt to restart the resource (used internally for some
* remote connection failures).
@@ -194,139 +175,95 @@ enum action_fail_response {
pcmk_on_fail_reset_remote = 8,
// @TODO Define as 20
- pcmk_on_fail_demote = 9, //!< Demote if promotable, else stop
+ pcmk_on_fail_demote = 9, // Demote if promotable, else stop
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_on_fail_ignore instead
action_fail_ignore = pcmk_on_fail_ignore,
-
- //! \deprecated Use pcmk_on_fail_restart instead
action_fail_recover = pcmk_on_fail_restart,
-
- //! \deprecated Use pcmk_on_fail_ban instead
action_fail_migrate = pcmk_on_fail_ban,
-
- //! \deprecated Use pcmk_on_fail_block instead
action_fail_block = pcmk_on_fail_block,
-
- //! \deprecated Use pcmk_on_fail_stop instead
action_fail_stop = pcmk_on_fail_stop,
-
- //! \deprecated Use pcmk_on_fail_standby_node instead
action_fail_standby = pcmk_on_fail_standby_node,
-
- //! \deprecated Use pcmk_on_fail_fence_node instead
action_fail_fence = pcmk_on_fail_fence_node,
-
- //! \deprecated Use pcmk_on_fail_restart_container instead
action_fail_restart_container = pcmk_on_fail_restart_container,
-
- //! \deprecated Use pcmk_on_fail_reset_remote instead
action_fail_reset_remote = pcmk_on_fail_reset_remote,
-
- //! \deprecated Use pcmk_on_fail_demote instead
action_fail_demote = pcmk_on_fail_demote,
#endif
};
+//!@}
-//! Action scheduling flags
+// Action scheduling flags
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum pe_action_flags {
- //! No action flags set (compare with equality rather than bit set)
+ // No action flags set (compare with equality rather than bit set)
pcmk_no_action_flags = 0,
- //! Whether action does not require invoking an agent
+ // Whether action does not require invoking an agent
pcmk_action_pseudo = (1 << 0),
- //! Whether action is runnable
+ // Whether action is runnable
pcmk_action_runnable = (1 << 1),
- //! Whether action should not be executed
+ // Whether action should not be executed
pcmk_action_optional = (1 << 2),
- //! Whether action should be added to transition graph even if optional
+ // Whether action should be added to transition graph even if optional
pcmk_action_always_in_graph = (1 << 3),
- //! Whether operation-specific instance attributes have been unpacked yet
+ // Whether operation-specific instance attributes have been unpacked yet
pcmk_action_attrs_evaluated = (1 << 4),
- //! Whether action is allowed to be part of a live migration
+ // Whether action is allowed to be part of a live migration
pcmk_action_migratable = (1 << 7),
- //! Whether action has been added to transition graph
+ // Whether action has been added to transition graph
pcmk_action_added_to_graph = (1 << 8),
- //! Whether action is a stop to abort a dangling migration
+ // Whether action is a stop to abort a dangling migration
pcmk_action_migration_abort = (1 << 11),
- /*!
+ /*
* Whether action is an ordering point for minimum required instances
- * (used to implement ordering after clones with clone-min configured,
- * and ordered sets with require-all=false)
+ * (used to implement ordering after clones with \c PCMK_META_CLONE_MIN
+ * configured, and ordered sets with \c PCMK_XA_REQUIRE_ALL set to
+ * \c PCMK_VALUE_FALSE).
*/
pcmk_action_min_runnable = (1 << 12),
- //! Whether action is recurring monitor that must be rescheduled if active
+ // Whether action is recurring monitor that must be rescheduled if active
pcmk_action_reschedule = (1 << 13),
- //! Whether action has already been processed by a recursive procedure
+ // Whether action has already been processed by a recursive procedure
pcmk_action_detect_loop = (1 << 14),
- //! Whether action's inputs have been de-duplicated yet
+ // Whether action's inputs have been de-duplicated yet
pcmk_action_inputs_deduplicated = (1 << 15),
- //! Whether action can be executed on DC rather than own node
+ // Whether action can be executed on DC rather than own node
pcmk_action_on_dc = (1 << 16),
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_action_pseudo instead
pe_action_pseudo = pcmk_action_pseudo,
-
- //! \deprecated Use pcmk_action_runnable instead
pe_action_runnable = pcmk_action_runnable,
-
- //! \deprecated Use pcmk_action_optional instead
pe_action_optional = pcmk_action_optional,
-
- //! \deprecated Use pcmk_action_always_in_graph instead
pe_action_print_always = pcmk_action_always_in_graph,
-
- //! \deprecated Use pcmk_action_attrs_evaluated instead
pe_action_have_node_attrs = pcmk_action_attrs_evaluated,
-
- //! \deprecated Do not use
pe_action_implied_by_stonith = (1 << 6),
-
- //! \deprecated Use pcmk_action_migratable instead
pe_action_migrate_runnable = pcmk_action_migratable,
-
- //! \deprecated Use pcmk_action_added_to_graph instead
pe_action_dumped = pcmk_action_added_to_graph,
-
- //! \deprecated Do not use
pe_action_processed = (1 << 9),
-
- //! \deprecated Do not use
pe_action_clear = (1 << 10),
-
- //! \deprecated Use pcmk_action_migration_abort instead
pe_action_dangle = pcmk_action_migration_abort,
-
- //! \deprecated Use pcmk_action_min_runnable instead
pe_action_requires_any = pcmk_action_min_runnable,
-
- //! \deprecated Use pcmk_action_reschedule instead
pe_action_reschedule = pcmk_action_reschedule,
-
- //! \deprecated Use pcmk_action_detect_loop instead
pe_action_tracking = pcmk_action_detect_loop,
-
- //! \deprecated Use pcmk_action_inputs_deduplicated instead
pe_action_dedup = pcmk_action_inputs_deduplicated,
-
- //! \deprecated Use pcmk_action_on_dc instead
pe_action_dc = pcmk_action_on_dc,
#endif
};
+//!@}
/* @COMPAT enum pe_link_state and enum pe_ordering are currently needed for
* struct pe_action_wrapper_s (which is public) but should be removed at an
@@ -386,40 +323,43 @@ struct pe_action_wrapper_s {
};
//!@}
-//! Implementation of pcmk_action_t
+// Implementation of pcmk_action_t
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
struct pe_action_s {
- int id; //!< Counter to identify action
+ int id; // Counter to identify action
- /*!
+ /*
* When the controller aborts a transition graph, it sets an abort priority.
* If this priority is higher, the action will still be executed anyway.
* Pseudo-actions are always allowed, so this is irrelevant for them.
*/
int priority;
- pcmk_resource_t *rsc; //!< Resource to apply action to, if any
- pcmk_node_t *node; //!< Node to execute action on, if any
- xmlNode *op_entry; //!< Action XML configuration, if any
- char *task; //!< Action name
- char *uuid; //!< Action key
- char *cancel_task; //!< If task is "cancel", the action being cancelled
- char *reason; //!< Readable description of why action is needed
+ pcmk_resource_t *rsc; // Resource to apply action to, if any
+ pcmk_node_t *node; // Node to execute action on, if any
+ xmlNode *op_entry; // Action XML configuration, if any
+ char *task; // Action name
+ char *uuid; // Action key
+ char *cancel_task; // If task is "cancel", the action being cancelled
+ char *reason; // Readable description of why action is needed
//@ COMPAT Change to uint32_t at a compatibility break
- enum pe_action_flags flags; //!< Group of enum pe_action_flags
+ enum pe_action_flags flags; // Group of enum pe_action_flags
- enum rsc_start_requirement needs; //!< Prerequisite for recovery
- enum action_fail_response on_fail; //!< Response to failure
- enum rsc_role_e fail_role; //!< Resource role if action fails
- GHashTable *meta; //!< Meta-attributes relevant to action
- GHashTable *extra; //!< Action-specific instance attributes
+ enum rsc_start_requirement needs; // Prerequisite for recovery
+ enum action_fail_response on_fail; // Response to failure
+ enum rsc_role_e fail_role; // Resource role if action fails
+ GHashTable *meta; // Meta-attributes relevant to action
+ GHashTable *extra; // Action-specific instance attributes
/* Current count of runnable instance actions for "first" action in an
* ordering dependency with pcmk__ar_min_runnable set.
*/
- int runnable_before; //!< For Pacemaker use only
+ int runnable_before; // For Pacemaker use only
- /*!
+ /*
* Number of instance actions for "first" action in an ordering dependency
* with pcmk__ar_min_runnable set that must be runnable before this action
* can be runnable.
@@ -427,15 +367,28 @@ struct pe_action_s {
int required_runnable_before;
// Actions in a relation with this one (as pcmk__related_action_t *)
- GList *actions_before; //!< For Pacemaker use only
- GList *actions_after; //!< For Pacemaker use only
+ GList *actions_before;
+ GList *actions_after;
/* This is intended to hold data that varies by the type of action, but is
* not currently used. Some of the above fields could be moved here except
* for API backward compatibility.
*/
- void *action_details; //!< For Pacemaker use only
+ void *action_details;
};
+//!@}
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
+const char *pcmk_action_text(enum action_tasks action);
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
+enum action_tasks pcmk_parse_action(const char *action_name);
+
+// @COMPAT Make this internal when we can break API backward compatibility
+//! \deprecated Do not use (public access will be removed in a future release)
+const char *pcmk_on_fail_text(enum action_fail_response on_fail);
// For parsing various action-related string specifications
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type,
diff --git a/include/crm/common/actions_internal.h b/include/crm/common/actions_internal.h
index 7e794e6..4a148f0 100644
--- a/include/crm/common/actions_internal.h
+++ b/include/crm/common/actions_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -21,9 +21,78 @@
extern "C" {
#endif
+// Action names as strings
+
+// @COMPAT Deprecated since 2.0.0
+#define PCMK__ACTION_POWEROFF "poweroff"
+
+
//! printf-style format to create operation key from resource, action, interval
#define PCMK__OP_FMT "%s_%s_%u"
+/*!
+ * \internal
+ * \brief Set action flags for an action
+ *
+ * \param[in,out] action Action to set flags for
+ * \param[in] flags_to_set Group of enum pe_action_flags to set
+ */
+#define pcmk__set_action_flags(action, flags_to_set) do { \
+ (action)->flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, \
+ "Action", (action)->uuid, \
+ (action)->flags, \
+ (flags_to_set), \
+ #flags_to_set); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Clear action flags for an action
+ *
+ * \param[in,out] action Action to clear flags for
+ * \param[in] flags_to_clear Group of enum pe_action_flags to clear
+ */
+#define pcmk__clear_action_flags(action, flags_to_clear) do { \
+ (action)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
+ LOG_TRACE, \
+ "Action", (action)->uuid, \
+ (action)->flags, \
+ (flags_to_clear), \
+ #flags_to_clear); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Set action flags for a flag group
+ *
+ * \param[in,out] action_flags Flag group to set flags for
+ * \param[in] action_name Name of action being modified (for logging)
+ * \param[in] to_set Group of enum pe_action_flags to set
+ */
+#define pcmk__set_raw_action_flags(action_flags, action_name, to_set) do { \
+ action_flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Action", action_name, \
+ (action_flags), \
+ (to_set), #to_set); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Clear action flags for a flag group
+ *
+ * \param[in,out] action_flags Flag group to clear flags for
+ * \param[in] action_name Name of action being modified (for logging)
+ * \param[in] to_clear Group of enum pe_action_flags to clear
+ */
+#define pcmk__clear_raw_action_flags(action_flags, action_name, to_clear) \
+ do { \
+ action_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \
+ "Action", action_name, \
+ (action_flags), \
+ (to_clear), #to_clear); \
+ } while (0)
+
char *pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms);
char *pcmk__notify_key(const char *rsc_id, const char *notify_type,
const char *op_type);
diff --git a/include/crm/common/agents.h b/include/crm/common/agents.h
index 5a67a87..7d7733f 100644
--- a/include/crm/common/agents.h
+++ b/include/crm/common/agents.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 the Pacemaker project contributors
+ * Copyright 2017-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_AGENTS__H
-# define PCMK__CRM_COMMON_AGENTS__H
+#define PCMK__CRM_COMMON_AGENTS__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/agents_compat.h b/include/crm/common/agents_compat.h
index 05a80f1..1c819a8 100644
--- a/include/crm/common/agents_compat.h
+++ b/include/crm/common/agents_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 the Pacemaker project contributors
+ * Copyright 2017-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_AGENTS_COMPAT__H
-# define PCMK__CRM_COMMON_AGENTS_COMPAT__H
+#define PCMK__CRM_COMMON_AGENTS_COMPAT__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h
index dc67427..3a591e5 100644
--- a/include/crm/common/alerts_internal.h
+++ b/include/crm/common/alerts_internal.h
@@ -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.
*
@@ -14,10 +14,10 @@
#include <stdbool.h>
/* Default-Timeout to use before killing a alerts script (in milliseconds) */
-# define PCMK__ALERT_DEFAULT_TIMEOUT_MS (30000)
+#define PCMK__ALERT_DEFAULT_TIMEOUT_MS (30000)
/* Default-Format-String used to pass timestamps to the alerts scripts */
-# define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N"
+#define PCMK__ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N"
enum pcmk__alert_flags {
pcmk__alert_none = 0,
diff --git a/include/crm/common/attrd_internal.h b/include/crm/common/attrs_internal.h
index 9d0b730..4c16675 100644
--- a/include/crm/common/attrd_internal.h
+++ b/include/crm/common/attrs_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -7,8 +7,13 @@
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
-#ifndef PCMK__ATTRD_INTERNAL__H
-# define PCMK__ATTRD_INTERNAL__H
+#ifndef PCMK__CRM_COMMON_ATTRS_INTERNAL__H
+#define PCMK__CRM_COMMON_ATTRS_INTERNAL__H
+
+#include <crm/crm.h> // crm_system_name
+#include <crm/common/logging.h> // LOG_TRACE
+#include <crm/common/scheduler_types.h> // pcmk_node_t
+#include <crm/common/failcounts_internal.h> // enum pcmk__rsc_node
#ifdef __cplusplus
extern "C" {
@@ -42,9 +47,11 @@ enum pcmk__node_attr_opts {
} while (0)
const char *pcmk__node_attr_target(const char *name);
+const char *pcmk__node_attr(const pcmk_node_t *node, const char *name,
+ const char *target, enum pcmk__rsc_node node_type);
#ifdef __cplusplus
}
#endif
-#endif
+#endif // PCMK__CRM_COMMON_ATTRS_INTERNAL__H
diff --git a/include/crm/common/bundles_internal.h b/include/crm/common/bundles_internal.h
new file mode 100644
index 0000000..62fdb74
--- /dev/null
+++ b/include/crm/common/bundles_internal.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-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.
+ */
+
+#ifndef PCMK__CRM_COMMON_BUNDLES_INTERNAL__H
+#define PCMK__CRM_COMMON_BUNDLES_INTERNAL__H
+
+#include <stdbool.h> // bool, false
+
+#include <crm/common/remote_internal.h> // pcmk__is_guest_or_bundle_node()
+#include <crm/common/resources.h> // pcmk_rsc_variant_bundle
+#include <crm/common/scheduler_types.h> // pcmk_resource_t, pcmk_node_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//! A single instance of a bundle
+typedef struct {
+ int offset; //!< 0-origin index of this instance in bundle
+ char *ipaddr; //!< IP address associated with this instance
+ pcmk_node_t *node; //!< Node created for this instance
+ pcmk_resource_t *ip; //!< IP address resource for ipaddr
+ pcmk_resource_t *child; //!< Instance of bundled resource
+ pcmk_resource_t *container; //!< Container associated with this instance
+ pcmk_resource_t *remote; //!< Pacemaker Remote connection into container
+} pcmk__bundle_replica_t;
+
+/*!
+ * \internal
+ * \brief Check whether a resource is a bundle resource
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is a bundle, otherwise false
+ * \note This does not return true if \p rsc is part of a bundle
+ * (see pcmk__is_bundled()).
+ */
+static inline bool
+pcmk__is_bundle(const pcmk_resource_t *rsc)
+{
+ return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle);
+}
+
+/*!
+ * \internal
+ * \brief Check whether a resource is part of a bundle
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is part of a bundle, otherwise false
+ */
+static inline bool
+pcmk__is_bundled(const pcmk_resource_t *rsc)
+{
+ if (rsc == NULL) {
+ return false;
+ }
+ while (rsc->parent != NULL) {
+ rsc = rsc->parent;
+ }
+ return rsc->variant == pcmk_rsc_variant_bundle;
+}
+
+/*!
+ * \internal
+ * \brief Check whether a node is a bundle node
+ *
+ * \param[in] node Node to check
+ *
+ * \return true if \p node is a bundle node, otherwise false
+ */
+static inline bool
+pcmk__is_bundle_node(const pcmk_node_t *node)
+{
+ return pcmk__is_guest_or_bundle_node(node)
+ && pcmk__is_bundled(node->details->remote_rsc);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_BUNDLES_INTERNAL__H
diff --git a/include/crm/common/cib.h b/include/crm/common/cib.h
index e1c4471..a30a22c 100644
--- a/include/crm/common/cib.h
+++ b/include/crm/common/cib.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_CIB__H
-# define PCMK__CRM_COMMON_CIB__H
+#define PCMK__CRM_COMMON_CIB__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/cib_internal.h b/include/crm/common/cib_internal.h
index c41c12e..fa65e58 100644
--- a/include/crm/common/cib_internal.h
+++ b/include/crm/common/cib_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 the Pacemaker project contributors
+ * Copyright 2023-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -16,6 +16,8 @@ extern "C" {
const char *pcmk__cib_abs_xpath_for(const char *element);
+int pcmk__check_feature_set(const char *cib_version);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/clone_internal.h b/include/crm/common/clone_internal.h
index 494ee74..db001a0 100644
--- a/include/crm/common/clone_internal.h
+++ b/include/crm/common/clone_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,11 @@
*/
#ifndef PCMK__CRM_COMMON_CLONE_INTERNAL__H
-# define PCMK__CRM_COMMON_CLONE_INTERNAL__H
+#define PCMK__CRM_COMMON_CLONE_INTERNAL__H
+
+#include <stdbool.h> // bool
+#include <crm/common/scheduler_types.h> // pcmk_resource_t
+#include <crm/common/resources.h> // pcmk_rsc_variant_clone
#ifdef __cplusplus
extern "C" {
@@ -26,6 +30,50 @@ enum pcmk__clone_flags {
pcmk__clone_promotion_constrained = (1 << 2),
};
+/*!
+ * \internal
+ * \brief Check whether a resource is a clone resource
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is a clone, otherwise false
+ *
+ * \note This does not return true if \p rsc has a clone ancestor.
+ */
+static inline bool
+pcmk__is_clone(const pcmk_resource_t *rsc)
+{
+ return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_clone);
+}
+
+/*!
+ * \internal
+ * \brief Check whether a resource is a globally unique clone
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is a unique clone, otherwise false
+ */
+static inline bool
+pcmk__is_unique_clone(const pcmk_resource_t *rsc)
+{
+ return pcmk__is_clone(rsc) && pcmk_is_set(rsc->flags, pcmk_rsc_unique);
+}
+
+/*!
+ * \internal
+ * \brief Check whether a resource is an anonymous clone
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is an anonymous clone, otherwise false
+ */
+static inline bool
+pcmk__is_anonymous_clone(const pcmk_resource_t *rsc)
+{
+ return pcmk__is_clone(rsc) && !pcmk_is_set(rsc->flags, pcmk_rsc_unique);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/digests_internal.h b/include/crm/common/digests_internal.h
index 7598de2..1b17cd6 100644
--- a/include/crm/common/digests_internal.h
+++ b/include/crm/common/digests_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_DIGESTS_INTERNAL__H
-# define PCMK__CRM_COMMON_DIGESTS_INTERNAL__H
+#define PCMK__CRM_COMMON_DIGESTS_INTERNAL__H
#include <libxml/tree.h> // xmlNode
@@ -24,6 +24,17 @@ enum pcmk__digest_result {
pcmk__digest_restart, // Parameters that require a restart changed
};
+// Information needed to compare operation digests
+typedef struct {
+ enum pcmk__digest_result rc; // Result of digest comparison
+ xmlNode *params_all; // All operation parameters
+ xmlNode *params_secure; // Parameters marked private
+ xmlNode *params_restart; // Parameters marked not reloadable
+ char *digest_all_calc; // Digest of params_all
+ char *digest_secure_calc; // Digest of params_secure
+ char *digest_restart_calc; // Digest of params_restart
+} pcmk__op_digest_t;
+
bool pcmk__verify_digest(xmlNode *input, const char *expected);
#ifdef __cplusplus
diff --git a/include/crm/common/failcounts_internal.h b/include/crm/common/failcounts_internal.h
index 4ad01bf..776f949 100644
--- a/include/crm/common/failcounts_internal.h
+++ b/include/crm/common/failcounts_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_FAILCOUNTS_INTERNAL__H
-# define PCMK__CRM_COMMON_FAILCOUNTS_INTERNAL__H
+#define PCMK__CRM_COMMON_FAILCOUNTS_INTERNAL__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/group_internal.h b/include/crm/common/group_internal.h
index 9e1424d..4a02e29 100644
--- a/include/crm/common/group_internal.h
+++ b/include/crm/common/group_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,11 @@
*/
#ifndef PCMK__CRM_COMMON_GROUP_INTERNAL__H
-# define PCMK__CRM_COMMON_GROUP_INTERNAL__H
+#define PCMK__CRM_COMMON_GROUP_INTERNAL__H
+
+#include <stdbool.h> // bool
+#include <crm/common/scheduler_types.h> // pcmk_resource_t
+#include <crm/common/resources.h> // pcmk_rsc_variant_group
#ifdef __cplusplus
extern "C" {
@@ -20,6 +24,22 @@ enum pcmk__group_flags {
pcmk__group_colocated = (1 << 1), // Members must be on same node
};
+/*!
+ * \internal
+ * \brief Check whether a resource is a group resource
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is a group, otherwise false
+ *
+ * \note This does not return true if \p rsc is a clone of a group.
+ */
+static inline bool
+pcmk__is_group(const pcmk_resource_t *rsc)
+{
+ return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/history_internal.h b/include/crm/common/history_internal.h
new file mode 100644
index 0000000..c74ef29
--- /dev/null
+++ b/include/crm/common/history_internal.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_HISTORY_INTERNAL__H
+#define PCMK__CRM_COMMON_HISTORY_INTERNAL__H
+
+#include <stdio.h> // NULL
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/common/xml.h> // crm_element_value()
+#include <crm/common/internal.h> // pcmk__str_empty()
+#include <crm/common/xml_names_internal.h> // PCMK__XA_OPERATION_KEY
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \internal
+ * \brief Get the operation key from an action history entry
+ *
+ * \param[in] xml Action history entry
+ *
+ * \return Entry's operation key
+ */
+static inline const char *
+pcmk__xe_history_key(const xmlNode *xml)
+{
+ if (xml == NULL) {
+ return NULL;
+ } else {
+ /* @COMPAT Pacemaker <= 1.1.5 did not add the key, and used the ID
+ * instead. Checking for that allows us to process old saved CIBs,
+ * including some regression tests.
+ */
+ const char *key = crm_element_value(xml, PCMK__XA_OPERATION_KEY);
+
+ return pcmk__str_empty(key)? pcmk__xe_id(xml) : key;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_HISTORY_INTERNAL__H
diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h
index 3078606..878ec4c 100644
--- a/include/crm/common/internal.h
+++ b/include/crm/common/internal.h
@@ -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.
*
@@ -27,6 +27,8 @@
#include <crm/common/iso8601_internal.h>
#include <crm/common/results_internal.h>
#include <crm/common/messages_internal.h>
+#include <crm/common/nvpair_internal.h>
+#include <crm/common/scores_internal.h>
#include <crm/common/strings_internal.h>
#include <crm/common/acl_internal.h>
@@ -98,7 +100,7 @@ pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value);
* \param[in] name XML attribute to get
*
* \return True if the given \p name is an attribute on \p node and has
- * the value "true", False in all other cases
+ * the value \c PCMK_VALUE_TRUE, False in all other cases
*/
bool
pcmk__xe_attr_is_true(const xmlNode *node, const char *name);
@@ -129,12 +131,6 @@ unsigned int pcmk__procfs_num_cores(void);
int pcmk__procfs_pid2path(pid_t pid, char path[], size_t path_size);
bool pcmk__procfs_has_pids(void);
-/* internal XML schema functions (from xml.c) */
-
-void crm_schema_init(void);
-void crm_schema_cleanup(void);
-
-
/* internal functions related to process IDs (from pid.c) */
/*!
@@ -231,6 +227,22 @@ pcmk__clear_flags_as(const char *function, int line, uint8_t log_level,
return result;
}
+/*!
+ * \internal
+ * \brief Get readable string for whether specified flags are set
+ *
+ * \param[in] flag_group Group of flags to check
+ * \param[in] flags Which flags in \p flag_group should be checked
+ *
+ * \return "true" if all \p flags are set in \p flag_group, otherwise "false"
+ */
+static inline const char *
+pcmk__flag_text(uint64_t flag_group, uint64_t flags)
+{
+ return pcmk__btoa(pcmk_all_flags_set(flag_group, flags));
+}
+
+
// miscellaneous utilities (from utils.c)
void pcmk__daemonize(const char *name, const char *pidfile);
@@ -244,6 +256,49 @@ extern int pcmk__score_yellow;
/*!
* \internal
+ * \brief Allocate new zero-initialized memory, 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] nmemb Number of elements to allocate memory for
+ * \param[in] size Size of each element
+ *
+ * \return Newly allocated memory of of size <tt>nmemb * size</tt> (guaranteed
+ * not to be \c NULL)
+ *
+ * \note The caller is responsible for freeing the return value using \c free().
+ */
+static inline void *
+pcmk__assert_alloc_as(const char *file, const char *function, uint32_t line,
+ size_t nmemb, size_t size)
+{
+ void *ptr = calloc(nmemb, size);
+
+ if (ptr == NULL) {
+ crm_abort(file, function, line, "Out of memory", FALSE, TRUE);
+ crm_exit(CRM_EX_OSERR);
+ }
+ return ptr;
+}
+
+/*!
+ * \internal
+ * \brief Allocate new zero-initialized memory, asserting on failure
+ *
+ * \param[in] nmemb Number of elements to allocate memory for
+ * \param[in] size Size of each element
+ *
+ * \return Newly allocated memory of of size <tt>nmemb * size</tt> (guaranteed
+ * not to be \c NULL)
+ *
+ * \note The caller is responsible for freeing the return value using \c free().
+ */
+#define pcmk__assert_alloc(nmemb, size) \
+ pcmk__assert_alloc_as(__FILE__, __func__, __LINE__, nmemb, size)
+
+/*!
+ * \internal
* \brief Resize a dynamically allocated memory block
*
* \param[in] ptr Memory block to resize (or NULL to allocate new memory)
@@ -270,7 +325,6 @@ pcmk__realloc(void *ptr, size_t size)
return new_ptr;
}
-
static inline char *
pcmk__getpid_s(void)
{
diff --git a/include/crm/common/io_internal.h b/include/crm/common/io_internal.h
index a8e1f28..6b6ef33 100644
--- a/include/crm/common/io_internal.h
+++ b/include/crm/common/io_internal.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IO_INTERNAL__H
-# define PCMK__CRM_COMMON_IO_INTERNAL__H
+#define PCMK__CRM_COMMON_IO_INTERNAL__H
#include <fcntl.h> // open()
#include <stdbool.h> // bool
diff --git a/include/crm/common/ipc.h b/include/crm/common/ipc.h
index 397c8b1..7a58725 100644
--- a/include/crm/common/ipc.h
+++ b/include/crm/common/ipc.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IPC__H
-# define PCMK__CRM_COMMON_IPC__H
+#define PCMK__CRM_COMMON_IPC__H
#include <sys/uio.h>
@@ -34,16 +34,24 @@ extern "C" {
* compatibility.
*/
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
#define create_reply(request, xml_response_data) \
create_reply_adv(request, xml_response_data, __func__)
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
xmlNode *create_reply_adv(const xmlNode *request, xmlNode *xml_response_data,
const char *origin);
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
#define create_request(task, xml_data, host_to, sys_to, sys_from, uuid_from) \
create_request_adv(task, xml_data, host_to, sys_to, sys_from, uuid_from, \
__func__)
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
xmlNode *create_request_adv(const char *task, xmlNode *xml_data,
const char *host_to, const char *sys_to,
const char *sys_from, const char *uuid_from,
@@ -76,11 +84,17 @@ enum pcmk_ipc_server {
pcmk_ipc_schedulerd, //!< Scheduler
};
+// NOTE: sbd (as of at least 1.5.2) uses this enum
//! Possible event types that an IPC event callback can be called for
enum pcmk_ipc_event {
pcmk_ipc_event_connect, //!< Result of asynchronous connection attempt
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_ipc_event_disconnect, //!< Termination of IPC connection
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_ipc_event_reply, //!< Daemon's reply to client IPC request
+
pcmk_ipc_event_notify, //!< Notification from daemon
};
@@ -91,6 +105,7 @@ enum pcmk_ipc_dispatch {
pcmk_ipc_dispatch_sync, //!< Sending a command will wait for any reply
};
+// NOTE: sbd (as of at least 1.5.2) uses this
//! Client connection to Pacemaker IPC
typedef struct pcmk_ipc_api_s pcmk_ipc_api_t;
@@ -113,10 +128,13 @@ typedef void (*pcmk_ipc_callback_t)(pcmk_ipc_api_t *api,
crm_exit_t status,
void *event_data, void *user_data);
+// NOTE: sbd (as of at least 1.5.2) uses this
int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server);
+// NOTE: sbd (as of at least 1.5.2) uses this
void pcmk_free_ipc_api(pcmk_ipc_api_t *api);
+// NOTE: sbd (as of at least 1.5.2) uses this
int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type);
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api);
@@ -125,6 +143,7 @@ int pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms);
void pcmk_dispatch_ipc(pcmk_ipc_api_t *api);
+// NOTE: sbd (as of at least 1.5.2) uses this
void pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb,
void *user_data);
@@ -220,9 +239,8 @@ unsigned int crm_ipc_default_buffer_size(void);
int crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
pid_t *gotpid, uid_t *gotuid, gid_t *gotgid);
-/* This is controller-specific but is declared in this header for C API
- * backward compatibility.
- */
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
xmlNode *create_hello_message(const char *uuid, const char *client_name,
const char *major_version, const char *minor_version);
diff --git a/include/crm/common/ipc_attrd_internal.h b/include/crm/common/ipc_attrd_internal.h
index b1b7584..de88e40 100644
--- a/include/crm/common/ipc_attrd_internal.h
+++ b/include/crm/common/ipc_attrd_internal.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
-# define PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
+#define PCMK__CRM_COMMON_IPC_ATTRD_INTERNAL__H
#include <glib.h> // GList
#include <crm/common/ipc.h> // pcmk_ipc_api_t
@@ -89,10 +89,11 @@ int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *na
/*!
* \internal
- * \brief Purge a node from pacemaker-attrd
+ * \brief Request removal of a node's transient attributes
*
* \param[in,out] api pacemaker-attrd IPC object
- * \param[in] node Node to remove
+ * \param[in] node Node whose attributes should be purged
+ * \param[in] reap If true, also request removal from node caches
*
* \note If \p api is NULL, a new temporary connection will be created
* just for this operation and destroyed afterwards. If \p api is
@@ -102,7 +103,7 @@ int pcmk__attrd_api_delete(pcmk_ipc_api_t *api, const char *node, const char *na
*
* \return Standard Pacemaker return code
*/
-int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node);
+int pcmk__attrd_api_purge(pcmk_ipc_api_t *api, const char *node, bool reap);
/*!
* \internal
diff --git a/include/crm/common/ipc_controld.h b/include/crm/common/ipc_controld.h
index 6deba48..cb4baac 100644
--- a/include/crm/common/ipc_controld.h
+++ b/include/crm/common/ipc_controld.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IPC_CONTROLD__H
-# define PCMK__CRM_COMMON_IPC_CONTROLD__H
+#define PCMK__CRM_COMMON_IPC_CONTROLD__H
#include <stdbool.h> // bool
diff --git a/include/crm/common/ipc_internal.h b/include/crm/common/ipc_internal.h
index b391e83..8a66126 100644
--- a/include/crm/common/ipc_internal.h
+++ b/include/crm/common/ipc_internal.h
@@ -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.
*
@@ -20,7 +20,7 @@ extern "C" {
#include <sys/types.h> // uid_t, gid_t, pid_t, size_t
#ifdef HAVE_GNUTLS_GNUTLS_H
-# include <gnutls/gnutls.h> // gnutls_session_t
+#include <gnutls/gnutls.h> // gnutls_session_t
#endif
#include <glib.h> // guint, gpointer, GQueue, ...
@@ -122,9 +122,9 @@ struct pcmk__remote_s {
char *token;
/* TLS only */
-# ifdef HAVE_GNUTLS_GNUTLS_H
+#ifdef HAVE_GNUTLS_GNUTLS_H
gnutls_session_t *tls_session;
-# endif
+#endif
};
enum pcmk__client_flags {
@@ -138,10 +138,10 @@ enum pcmk__client_flags {
//! Client uses TCP connection
pcmk__client_tcp = (UINT64_C(1) << 33),
-# ifdef HAVE_GNUTLS_GNUTLS_H
+#ifdef HAVE_GNUTLS_GNUTLS_H
//! Client uses TCP with TLS
pcmk__client_tls = (UINT64_C(1) << 34),
-# endif
+#endif
// The rest are client attributes
@@ -157,14 +157,14 @@ enum pcmk__client_flags {
/*!
* \brief Client IPC connection accepted
*
- * Used only for remote CIB connections via \c remote-tls-port.
+ * Used only for remote CIB connections via \c PCMK_XA_REMOTE_TLS_PORT.
*/
pcmk__client_authenticated = (UINT64_C(1) << 43),
-# ifdef HAVE_GNUTLS_GNUTLS_H
+#ifdef HAVE_GNUTLS_GNUTLS_H
//! Client TLS handshake is complete
pcmk__client_tls_handshake_complete = (UINT64_C(1) << 44),
-# endif
+#endif
};
#define PCMK__CLIENT_TYPE(client) ((client)->flags & UINT64_C(0xff00000000))
diff --git a/include/crm/common/ipc_pacemakerd.h b/include/crm/common/ipc_pacemakerd.h
index 340f9a6..39d50ea 100644
--- a/include/crm/common/ipc_pacemakerd.h
+++ b/include/crm/common/ipc_pacemakerd.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IPC_PACEMAKERD__H
-# define PCMK__CRM_COMMON_IPC_PACEMAKERD__H
+#define PCMK__CRM_COMMON_IPC_PACEMAKERD__H
#include <sys/types.h> // time_t
#include <crm/common/ipc.h> // pcmk_ipc_api_t
@@ -24,14 +24,22 @@ extern "C" {
* \ingroup core
*/
+// NOTE: sbd (as of at least 1.5.2) uses this enum
enum pcmk_pacemakerd_state {
pcmk_pacemakerd_state_invalid = -1,
pcmk_pacemakerd_state_init = 0,
pcmk_pacemakerd_state_starting_daemons,
pcmk_pacemakerd_state_wait_for_ping,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_pacemakerd_state_running,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_pacemakerd_state_shutting_down,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_pacemakerd_state_shutdown_complete,
+
pcmk_pacemakerd_state_remote,
pcmk_pacemakerd_state_max = pcmk_pacemakerd_state_remote,
};
@@ -39,10 +47,14 @@ enum pcmk_pacemakerd_state {
//! Possible types of pacemakerd replies
enum pcmk_pacemakerd_api_reply {
pcmk_pacemakerd_reply_unknown,
+
+ // NOTE: sbd (as of at least 1.5.2) uses this value
pcmk_pacemakerd_reply_ping,
+
pcmk_pacemakerd_reply_shutdown,
};
+// NOTE: sbd (as of at least 1.5.2) uses this type and some of its members
/*!
* Pacemakerd reply passed to event callback
*/
@@ -64,7 +76,9 @@ typedef struct {
} data;
} pcmk_pacemakerd_api_reply_t;
+// NOTE: sbd (as of at least 1.5.2) uses this
int pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name);
+
int pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name);
enum pcmk_pacemakerd_state
diff --git a/include/crm/common/ipc_schedulerd.h b/include/crm/common/ipc_schedulerd.h
index 303ec59..096e2d8 100644
--- a/include/crm/common/ipc_schedulerd.h
+++ b/include/crm/common/ipc_schedulerd.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_IPC_SCHEDULERD__H
-# define PCMK__CRM_COMMON_IPC_SCHEDULERD__H
+#define PCMK__CRM_COMMON_IPC_SCHEDULERD__H
#include <crm/common/ipc.h> // pcmk_ipc_api_t
diff --git a/include/crm/common/iso8601.h b/include/crm/common/iso8601.h
index 78f530b..4c6adda 100644
--- a/include/crm/common/iso8601.h
+++ b/include/crm/common/iso8601.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2020 the Pacemaker project contributors
+ * Copyright 2005-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,12 +8,12 @@
*/
#ifndef PCMK__CRM_COMMON_ISO8601__H
-# define PCMK__CRM_COMMON_ISO8601__H
+#define PCMK__CRM_COMMON_ISO8601__H
-# include <time.h>
-# include <ctype.h>
-# include <stdint.h> // uint32_t
-# include <stdbool.h> // bool
+#include <time.h>
+#include <ctype.h>
+#include <stdint.h> // uint32_t
+#include <stdbool.h> // bool
#ifdef __cplusplus
extern "C" {
@@ -64,16 +64,16 @@ void crm_time_log_alias(int log_level, const char *file, const char *function,
int line, const char *prefix,
const crm_time_t *date_time, int flags);
-# define crm_time_log_date 0x001
-# define crm_time_log_timeofday 0x002
-# define crm_time_log_with_timezone 0x004
-# define crm_time_log_duration 0x008
+#define crm_time_log_date 0x001
+#define crm_time_log_timeofday 0x002
+#define crm_time_log_with_timezone 0x004
+#define crm_time_log_duration 0x008
-# define crm_time_ordinal 0x010
-# define crm_time_weeks 0x020
-# define crm_time_seconds 0x100
-# define crm_time_epoch 0x200
-# define crm_time_usecs 0x400
+#define crm_time_ordinal 0x010
+#define crm_time_weeks 0x020
+#define crm_time_seconds 0x100
+#define crm_time_epoch 0x200
+#define crm_time_usecs 0x400
crm_time_t *crm_time_parse_duration(const char *duration_str);
crm_time_t *crm_time_calculate_duration(const crm_time_t *dt,
diff --git a/include/crm/common/iso8601_internal.h b/include/crm/common/iso8601_internal.h
index f924d8a..3093a32 100644
--- a/include/crm/common/iso8601_internal.h
+++ b/include/crm/common/iso8601_internal.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__ISO8601_INTERNAL__H
-# define PCMK__ISO8601_INTERNAL__H
+#define PCMK__ISO8601_INTERNAL__H
#include <time.h>
#include <sys/time.h>
diff --git a/include/crm/common/location_internal.h b/include/crm/common/location_internal.h
new file mode 100644
index 0000000..a00691e
--- /dev/null
+++ b/include/crm/common/location_internal.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_LOCATION_INTERNAL__H
+#define PCMK__CRM_COMMON_LOCATION_INTERNAL__H
+
+#include <glib.h> // GList
+
+#include <crm/common/nodes.h> // enum pe_discover_e
+#include <crm/common/resources.h> // enum rsc_role_e
+#include <crm/common/scheduler_types.h> // pcmk_resource_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//! Location constraint object
+typedef struct {
+ char *id; // XML ID of location constraint
+ pcmk_resource_t *rsc; // Resource with location preference
+ enum rsc_role_e role_filter; // Limit to instances with this role
+ enum pe_discover_e discover_mode; // How to probe resource on node
+ GList *nodes; // Affected nodes, with preference score
+} pcmk__location_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_LOCATION_INTERNAL__H
diff --git a/include/crm/common/logging.h b/include/crm/common/logging.h
index eea4cec..abc2fe8 100644
--- a/include/crm/common/logging.h
+++ b/include/crm/common/logging.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,13 +8,13 @@
*/
#ifndef PCMK__CRM_COMMON_LOGGING__H
-# define PCMK__CRM_COMMON_LOGGING__H
+#define PCMK__CRM_COMMON_LOGGING__H
-# include <stdio.h>
-# include <stdint.h> // uint8_t, uint32_t
-# include <glib.h>
-# include <qb/qblog.h>
-# include <libxml/tree.h>
+#include <stdio.h>
+#include <stdint.h> // uint8_t, uint32_t
+#include <glib.h>
+#include <qb/qblog.h>
+#include <libxml/tree.h>
#ifdef __cplusplus
extern "C" {
@@ -34,26 +34,26 @@ extern "C" {
*/
// Define something even less desired than debug
-# ifndef LOG_TRACE
-# define LOG_TRACE (LOG_DEBUG+1)
-# endif
+#ifndef LOG_TRACE
+#define LOG_TRACE (LOG_DEBUG+1)
+#endif
// Print message to stdout instead of logging it
-# ifndef LOG_STDOUT
-# define LOG_STDOUT 254
-# endif
+#ifndef LOG_STDOUT
+#define LOG_STDOUT 254
+#endif
// Don't send message anywhere
-# ifndef LOG_NEVER
-# define LOG_NEVER 255
-# endif
+#ifndef LOG_NEVER
+#define LOG_NEVER 255
+#endif
/* "Extended information" logging support */
#ifdef QB_XS
-# define CRM_XS QB_XS
-# define crm_extended_logging(t, e) qb_log_ctl((t), QB_LOG_CONF_EXTENDED, (e))
+#define CRM_XS QB_XS
+#define crm_extended_logging(t, e) qb_log_ctl((t), QB_LOG_CONF_EXTENDED, (e))
#else
-# define CRM_XS "|"
+#define CRM_XS "|"
/* A caller might want to check the return value, so we can't define this as a
* no-op, and we can't simply define it to be 0 because gcc will then complain
@@ -66,7 +66,12 @@ crm_extended_logging(int t, int e)
}
#endif
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
extern unsigned int crm_log_level;
+
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
extern unsigned int crm_trace_nonlog;
/*! \deprecated Pacemaker library functions set this when a configuration
@@ -116,6 +121,7 @@ void crm_enable_stderr(int enable);
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags);
+// NOTE: sbd (as of at least 1.5.2) uses this
/* returns the old value */
unsigned int set_crm_log_level(unsigned int level);
@@ -131,10 +137,10 @@ void pcmk_log_xml_as(const char *file, const char *function, uint32_t line,
* https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros
*/
#if defined(__clang__)
-# define CRM_TRACE_INIT_DATA(name)
-# else
-# include <assert.h> // required by QB_LOG_INIT_DATA() macro
-# define CRM_TRACE_INIT_DATA(name) QB_LOG_INIT_DATA(name)
+#define CRM_TRACE_INIT_DATA(name)
+#else
+#include <assert.h> // required by QB_LOG_INIT_DATA() macro
+#define CRM_TRACE_INIT_DATA(name) QB_LOG_INIT_DATA(name)
#endif
/*!
@@ -172,7 +178,7 @@ pcmk__clip_log_level(int level)
* \param[in] fmt printf-style format string literal for message
* \param[in] args Any arguments needed by format string
*/
-# define do_crm_log(level, fmt, args...) do { \
+#define do_crm_log(level, fmt, args...) do { \
uint8_t _level = pcmk__clip_log_level(level); \
\
switch (_level) { \
@@ -197,7 +203,7 @@ pcmk__clip_log_level(int level)
*
* \note This does nothing when level is \p LOG_STDOUT.
*/
-# define do_crm_log_unlikely(level, fmt, args...) do { \
+#define do_crm_log_unlikely(level, fmt, args...) do { \
uint8_t _level = pcmk__clip_log_level(level); \
\
switch (_level) { \
@@ -219,7 +225,7 @@ pcmk__clip_log_level(int level)
} \
} while (0)
-# define CRM_LOG_ASSERT(expr) do { \
+#define CRM_LOG_ASSERT(expr) do { \
if (!(expr)) { \
static struct qb_log_callsite *core_cs = NULL; \
if(core_cs == NULL) { \
@@ -232,10 +238,11 @@ pcmk__clip_log_level(int level)
} \
} while(0)
+// NOTE: sbd (as of at least 1.5.2) uses this
/* 'failure_action' MUST NOT be 'continue' as it will apply to the
* macro's do-while loop
*/
-# define CRM_CHECK(expr, failure_action) do { \
+#define CRM_CHECK(expr, failure_action) do { \
if (!(expr)) { \
static struct qb_log_callsite *core_cs = NULL; \
if (core_cs == NULL) { \
@@ -243,10 +250,10 @@ pcmk__clip_log_level(int level)
"check-assert", \
LOG_TRACE, __LINE__, 0); \
} \
- crm_abort(__FILE__, __func__, __LINE__, #expr, \
- (core_cs? core_cs->targets: FALSE), TRUE); \
- failure_action; \
- } \
+ crm_abort(__FILE__, __func__, __LINE__, #expr, \
+ (core_cs? core_cs->targets: FALSE), TRUE); \
+ failure_action; \
+ } \
} while(0)
/*!
@@ -258,7 +265,7 @@ pcmk__clip_log_level(int level)
*
* \note This does nothing when \p level is \p LOG_STDOUT.
*/
-# define do_crm_log_xml(level, text, xml) do { \
+#define do_crm_log_xml(level, text, xml) do { \
uint8_t _level = pcmk__clip_log_level(level); \
static struct qb_log_callsite *xml_cs = NULL; \
\
@@ -290,7 +297,7 @@ pcmk__clip_log_level(int level)
* \param[in] fmt printf-style format string literal for message
* \param[in] args Any arguments needed by format string
*/
-# define do_crm_log_alias(level, file, function, line, fmt, args...) do { \
+#define do_crm_log_alias(level, file, function, line, fmt, args...) do { \
uint8_t _level = pcmk__clip_log_level(level); \
\
switch (_level) { \
@@ -306,6 +313,7 @@ pcmk__clip_log_level(int level)
} \
} while (0)
+// NOTE: sbd (as of at least 1.5.2) uses this
/*!
* \brief Send a system error message to both the log and stderr
*
@@ -320,7 +328,7 @@ pcmk__clip_log_level(int level)
* onto the end of fmt, that information will become extended information
* if CRM_XS is used inside fmt and will not show up in syslog.
*/
-# define crm_perror(level, fmt, args...) do { \
+#define crm_perror(level, fmt, args...) do { \
uint8_t _level = pcmk__clip_log_level(level); \
\
switch (_level) { \
@@ -351,7 +359,7 @@ pcmk__clip_log_level(int level)
*
* \note This does nothing when level is LOG_STDOUT.
*/
-# define crm_log_tag(level, tag, fmt, args...) do { \
+#define crm_log_tag(level, tag, fmt, args...) do { \
uint8_t _level = pcmk__clip_log_level(level); \
\
switch (_level) { \
@@ -376,25 +384,34 @@ pcmk__clip_log_level(int level)
} \
} while (0)
-# define crm_emerg(fmt, args...) qb_log(LOG_EMERG, fmt , ##args)
-# define crm_crit(fmt, args...) qb_logt(LOG_CRIT, 0, fmt , ##args)
-# define crm_err(fmt, args...) qb_logt(LOG_ERR, 0, fmt , ##args)
-# define crm_warn(fmt, args...) qb_logt(LOG_WARNING, 0, fmt , ##args)
-# define crm_notice(fmt, args...) qb_logt(LOG_NOTICE, 0, fmt , ##args)
-# define crm_info(fmt, args...) qb_logt(LOG_INFO, 0, fmt , ##args)
-
-# define crm_debug(fmt, args...) do_crm_log_unlikely(LOG_DEBUG, fmt , ##args)
-# define crm_trace(fmt, args...) do_crm_log_unlikely(LOG_TRACE, fmt , ##args)
-
-# define crm_log_xml_crit(xml, text) do_crm_log_xml(LOG_CRIT, text, xml)
-# define crm_log_xml_err(xml, text) do_crm_log_xml(LOG_ERR, text, xml)
-# define crm_log_xml_warn(xml, text) do_crm_log_xml(LOG_WARNING, text, xml)
-# define crm_log_xml_notice(xml, text) do_crm_log_xml(LOG_NOTICE, text, xml)
-# define crm_log_xml_info(xml, text) do_crm_log_xml(LOG_INFO, text, xml)
-# define crm_log_xml_debug(xml, text) do_crm_log_xml(LOG_DEBUG, text, xml)
-# define crm_log_xml_trace(xml, text) do_crm_log_xml(LOG_TRACE, text, xml)
-
-# define crm_log_xml_explicit(xml, text) do { \
+#define crm_emerg(fmt, args...) qb_log(LOG_EMERG, fmt , ##args)
+#define crm_crit(fmt, args...) qb_logt(LOG_CRIT, 0, fmt , ##args)
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define crm_err(fmt, args...) qb_logt(LOG_ERR, 0, fmt , ##args)
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define crm_warn(fmt, args...) qb_logt(LOG_WARNING, 0, fmt , ##args)
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define crm_notice(fmt, args...) qb_logt(LOG_NOTICE, 0, fmt , ##args)
+
+#define crm_info(fmt, args...) qb_logt(LOG_INFO, 0, fmt , ##args)
+ //
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define crm_debug(fmt, args...) do_crm_log_unlikely(LOG_DEBUG, fmt , ##args)
+
+#define crm_trace(fmt, args...) do_crm_log_unlikely(LOG_TRACE, fmt , ##args)
+
+#define crm_log_xml_crit(xml, text) do_crm_log_xml(LOG_CRIT, text, xml)
+#define crm_log_xml_err(xml, text) do_crm_log_xml(LOG_ERR, text, xml)
+#define crm_log_xml_warn(xml, text) do_crm_log_xml(LOG_WARNING, text, xml)
+#define crm_log_xml_notice(xml, text) do_crm_log_xml(LOG_NOTICE, text, xml)
+#define crm_log_xml_info(xml, text) do_crm_log_xml(LOG_INFO, text, xml)
+#define crm_log_xml_debug(xml, text) do_crm_log_xml(LOG_DEBUG, text, xml)
+#define crm_log_xml_trace(xml, text) do_crm_log_xml(LOG_TRACE, text, xml)
+
+#define crm_log_xml_explicit(xml, text) do { \
static struct qb_log_callsite *digest_cs = NULL; \
digest_cs = qb_log_callsite_get( \
__func__, __FILE__, text, LOG_TRACE, __LINE__, \
diff --git a/include/crm/common/logging_compat.h b/include/crm/common/logging_compat.h
index b57a802..f6dec36 100644
--- a/include/crm/common/logging_compat.h
+++ b/include/crm/common/logging_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_LOGGING_COMPAT__H
-# define PCMK__CRM_COMMON_LOGGING_COMPAT__H
+#define PCMK__CRM_COMMON_LOGGING_COMPAT__H
#include <stdint.h> // uint8_t
#include <glib.h>
@@ -55,7 +55,7 @@ enum xml_log_options {
* \note This is a macro, and \p level may be evaluated more than once.
* This does nothing when level is LOG_STDOUT.
*/
-# define do_crm_log_always(level, fmt, args...) do { \
+#define do_crm_log_always(level, fmt, args...) do { \
switch (level) { \
case LOG_STDOUT: case LOG_NEVER: \
break; \
diff --git a/include/crm/common/logging_internal.h b/include/crm/common/logging_internal.h
index 981ddf3..3bfd504 100644
--- a/include/crm/common/logging_internal.h
+++ b/include/crm/common/logging_internal.h
@@ -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.
*
@@ -12,12 +12,70 @@ extern "C" {
#endif
#ifndef PCMK__LOGGING_INTERNAL_H
-# define PCMK__LOGGING_INTERNAL_H
+#define PCMK__LOGGING_INTERNAL_H
-# include <glib.h>
+#include <glib.h>
-# include <crm/common/logging.h>
-# include <crm/common/output_internal.h>
+#include <crm/common/logging.h>
+#include <crm/common/output_internal.h>
+
+/* Some warnings are too noisy when logged every time a given function is called
+ * (for example, using a deprecated feature). As an alternative, we allow
+ * warnings to be logged once per invocation of the calling program. Each of
+ * those warnings needs a flag defined here.
+ */
+enum pcmk__warnings {
+ pcmk__wo_blind = (1 << 0),
+ pcmk__wo_restart_type = (1 << 1),
+ pcmk__wo_role_after = (1 << 2),
+ pcmk__wo_poweroff = (1 << 3),
+ pcmk__wo_require_all = (1 << 4),
+ pcmk__wo_order_score = (1 << 5),
+ pcmk__wo_neg_threshold = (1 << 6),
+ pcmk__wo_remove_after = (1 << 7),
+ pcmk__wo_ping_node = (1 << 8),
+ pcmk__wo_order_inst = (1 << 9),
+ pcmk__wo_coloc_inst = (1 << 10),
+ pcmk__wo_group_order = (1 << 11),
+ pcmk__wo_group_coloc = (1 << 12),
+ pcmk__wo_upstart = (1 << 13),
+ pcmk__wo_nagios = (1 << 14),
+ pcmk__wo_set_ordering = (1 << 15),
+ pcmk__wo_rdisc_enabled = (1 << 16),
+ pcmk__wo_rkt = (1 << 17),
+ pcmk__wo_location_rules = (1 << 18),
+ pcmk__wo_op_attr_expr = (1 << 19),
+ pcmk__wo_instance_defaults = (1 << 20),
+ pcmk__wo_multiple_rules = (1 << 21),
+ pcmk__wo_master_element = (1 << 22),
+ pcmk__wo_clone_master_max = (1 << 23),
+ pcmk__wo_clone_master_node_max = (1 << 24),
+ pcmk__wo_bundle_master = (1 << 25),
+ pcmk__wo_master_role = (1 << 26),
+ pcmk__wo_slave_role = (1 << 27),
+};
+
+/*!
+ * \internal
+ * \brief Log a warning once per invocation of calling program
+ *
+ * \param[in] wo_flag enum pcmk__warnings value for this warning
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__warn_once(wo_flag, fmt...) do { \
+ if (!pcmk_is_set(pcmk__warnings, wo_flag)) { \
+ if (wo_flag == pcmk__wo_blind) { \
+ crm_warn(fmt); \
+ } else { \
+ pcmk__config_warn(fmt); \
+ } \
+ pcmk__warnings = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, \
+ "Warn-once", "logging", \
+ pcmk__warnings, \
+ (wo_flag), #wo_flag); \
+ } \
+ } while (0)
typedef void (*pcmk__config_error_func) (void *ctx, const char *msg, ...);
typedef void (*pcmk__config_warning_func) (void *ctx, const char *msg, ...);
@@ -33,12 +91,11 @@ void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler,
/*!
* \internal
- * \brief Log a configuration error
+ * \brief Log an error and make crm_verify return failure status
*
- * \param[in] fmt printf(3)-style format string
- * \param[in] ... Arguments for format string
+ * \param[in] fmt... printf(3)-style format string and arguments
*/
-# define pcmk__config_err(fmt...) do { \
+#define pcmk__config_err(fmt...) do { \
crm_config_error = TRUE; \
if (pcmk__config_error_handler == NULL) { \
crm_err(fmt); \
@@ -49,18 +106,17 @@ void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler,
/*!
* \internal
- * \brief Log a configuration warning
+ * \brief Log a warning and make crm_verify return failure status
*
- * \param[in] fmt printf(3)-style format string
- * \param[in] ... Arguments for format string
+ * \param[in] fmt... printf(3)-style format string and arguments
*/
-# define pcmk__config_warn(fmt...) do { \
- crm_config_warning = TRUE; \
- if (pcmk__config_warning_handler == NULL) { \
- crm_warn(fmt); \
- } else { \
- pcmk__config_warning_handler(pcmk__config_warning_context, fmt); \
- } \
+#define pcmk__config_warn(fmt...) do { \
+ crm_config_warning = TRUE; \
+ if (pcmk__config_warning_handler == NULL) { \
+ crm_warn(fmt); \
+ } else { \
+ pcmk__config_warning_handler(pcmk__config_warning_context, fmt);\
+ } \
} while (0)
/*!
@@ -76,7 +132,7 @@ void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler,
* \note Neither \p if_action nor \p else_action can contain a \p break or
* \p continue statement.
*/
-# define pcmk__if_tracing(if_action, else_action) do { \
+#define pcmk__if_tracing(if_action, else_action) do { \
static struct qb_log_callsite *trace_cs = NULL; \
\
if (trace_cs == NULL) { \
diff --git a/include/crm/common/mainloop.h b/include/crm/common/mainloop.h
index a55bcdf..522a945 100644
--- a/include/crm/common/mainloop.h
+++ b/include/crm/common/mainloop.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2022 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,11 +8,11 @@
*/
#ifndef PCMK__CRM_COMMON_MAINLOOP__H
-# define PCMK__CRM_COMMON_MAINLOOP__H
+#define PCMK__CRM_COMMON_MAINLOOP__H
-# include <signal.h> // sighandler_t
-# include <glib.h>
-# include <stdbool.h>
+#include <signal.h> // sighandler_t
+#include <glib.h>
+#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@@ -29,47 +29,57 @@ enum mainloop_child_flags {
mainloop_leave_pid_group = 0x01,
};
+// NOTE: sbd (as of at least 1.5.2) uses this
typedef struct trigger_s crm_trigger_t;
+
typedef struct mainloop_io_s mainloop_io_t;
typedef struct mainloop_child_s mainloop_child_t;
+
+// NOTE: sbd (as of at least 1.5.2) uses this
typedef struct mainloop_timer_s mainloop_timer_t;
void mainloop_cleanup(void);
+// NOTE: sbd (as of at least 1.5.2) uses this
crm_trigger_t *mainloop_add_trigger(int priority, int (*dispatch) (gpointer user_data),
gpointer userdata);
+// NOTE: sbd (as of at least 1.5.2) uses this
void mainloop_set_trigger(crm_trigger_t * source);
void mainloop_trigger_complete(crm_trigger_t * trig);
gboolean mainloop_destroy_trigger(crm_trigger_t * source);
-# ifndef HAVE_SIGHANDLER_T
+#ifndef HAVE_SIGHANDLER_T
typedef void (*sighandler_t)(int);
-# endif
+#endif
sighandler_t crm_signal_handler(int sig, sighandler_t dispatch);
+// NOTE: sbd (as of at least 1.5.2) uses this
gboolean mainloop_add_signal(int sig, void (*dispatch) (int sig));
gboolean mainloop_destroy_signal(int sig);
bool mainloop_timer_running(mainloop_timer_t *t);
+// NOTE: sbd (as of at least 1.5.2) uses this
void mainloop_timer_start(mainloop_timer_t *t);
+// NOTE: sbd (as of at least 1.5.2) uses this
void mainloop_timer_stop(mainloop_timer_t *t);
guint mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms);
+// NOTE: sbd (as of at least 1.5.2) uses this
mainloop_timer_t *mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata);
void mainloop_timer_del(mainloop_timer_t *t);
-# include <crm/common/ipc.h>
-# include <qb/qbipcs.h>
+#include <crm/common/ipc.h>
+#include <qb/qbipcs.h>
struct ipc_client_callbacks {
/*!
@@ -179,7 +189,7 @@ void pcmk_quit_main_loop(GMainLoop *mloop, unsigned int n);
void pcmk_drain_main_loop(GMainLoop *mloop, guint timer_ms,
bool (*check)(guint));
-# define G_PRIORITY_MEDIUM (G_PRIORITY_HIGH/2)
+#define G_PRIORITY_MEDIUM (G_PRIORITY_HIGH/2)
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/common/mainloop_compat.h>
diff --git a/include/crm/common/mainloop_compat.h b/include/crm/common/mainloop_compat.h
index 5eb445a..4c8e479 100644
--- a/include/crm/common/mainloop_compat.h
+++ b/include/crm/common/mainloop_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2021 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,10 +8,10 @@
*/
#ifndef PCMK__CRM_COMMON_MAINLOOP_COMPAT__H
-# define PCMK__CRM_COMMON_MAINLOOP_COMPAT__H
+#define PCMK__CRM_COMMON_MAINLOOP_COMPAT__H
-# include <glib.h>
-# include <stdbool.h>
+#include <glib.h>
+#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/messages_internal.h b/include/crm/common/messages_internal.h
index 0d1908f..79db784 100644
--- a/include/crm/common/messages_internal.h
+++ b/include/crm/common/messages_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-2022 the Pacemaker project contributors
+ * Copyright 2018-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,6 +14,7 @@
#include <libxml/tree.h> // xmlNode
#include <crm/common/ipc_internal.h> // pcmk__client_t
#include <crm/common/results_internal.h> // pcmk__action_result_t
+#include <crm/common/xml_internal.h> // pcmk__xml_copy()
enum pcmk__request_flags {
pcmk__request_none = UINT32_C(0),
diff --git a/include/crm/common/nodes.h b/include/crm/common/nodes.h
index fbc3758..5f6f25f 100644
--- a/include/crm/common/nodes.h
+++ b/include/crm/common/nodes.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,8 +8,9 @@
*/
#ifndef PCMK__CRM_COMMON_NODES__H
-# define PCMK__CRM_COMMON_NODES__H
+#define PCMK__CRM_COMMON_NODES__H
+#include <stdbool.h> // bool
#include <glib.h> // gboolean, GList, GHashTable
#include <crm/common/scheduler_types.h> // pcmk_resource_t, pcmk_scheduler_t
@@ -26,116 +27,201 @@ extern "C" {
// Special node attributes
-#define PCMK_NODE_ATTR_TERMINATE "terminate"
+#define PCMK_NODE_ATTR_MAINTENANCE "maintenance"
+#define PCMK_NODE_ATTR_STANDBY "standby"
+#define PCMK_NODE_ATTR_TERMINATE "terminate"
-//! Possible node types
-enum node_type {
- pcmk_node_variant_cluster = 1, //!< Cluster layer node
- pcmk_node_variant_remote = 2, //!< Pacemaker Remote node
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
+enum node_type { // Possible node types
+ pcmk_node_variant_cluster = 1, // Cluster layer node
+ pcmk_node_variant_remote = 2, // Pacemaker Remote node
- node_ping = 0, //!< \deprecated Do not use
+ node_ping = 0, // deprecated
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_node_variant_cluster instead
node_member = pcmk_node_variant_cluster,
-
- //! \deprecated Use pcmk_node_variant_remote instead
node_remote = pcmk_node_variant_remote,
#endif
};
+//!@}
-//! When to probe a resource on a node (as specified in location constraints)
+// When to probe a resource on a node (as specified in location constraints)
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
enum pe_discover_e {
- pcmk_probe_always = 0, //! Always probe resource on node
- pcmk_probe_never = 1, //! Never probe resource on node
- pcmk_probe_exclusive = 2, //! Probe only on designated nodes
+ pcmk_probe_always = 0, // Always probe resource on node
+ pcmk_probe_never = 1, // Never probe resource on node
+ pcmk_probe_exclusive = 2, // Probe only on designated nodes
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_probe_always instead
pe_discover_always = pcmk_probe_always,
-
- //! \deprecated Use pcmk_probe_never instead
pe_discover_never = pcmk_probe_never,
-
- //! \deprecated Use pcmk_probe_exclusive instead
pe_discover_exclusive = pcmk_probe_exclusive,
#endif
};
+//!@}
-//! Basic node information (all node objects for the same node share this)
+// Basic node information (all node objects for the same node share this)
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
struct pe_node_shared_s {
- const char *id; //!< Node ID at the cluster layer
- const char *uname; //!< Node name in cluster
- enum node_type type; //!< Node variant
+ const char *id; // Node ID at the cluster layer
+ const char *uname; // Node name in cluster
+ enum node_type type; // Node variant
// @TODO Convert these into a flag group
- gboolean online; //!< Whether online
- gboolean standby; //!< Whether in standby mode
- gboolean standby_onfail; //!< Whether in standby mode due to on-fail
- gboolean pending; //!< Whether controller membership is pending
- gboolean unclean; //!< Whether node requires fencing
- gboolean unseen; //!< Whether node has never joined cluster
- gboolean shutdown; //!< Whether shutting down
- gboolean expected_up; //!< Whether expected join state is member
- gboolean is_dc; //!< Whether node is cluster's DC
- gboolean maintenance; //!< Whether in maintenance mode
- gboolean rsc_discovery_enabled; //!< Whether probes are allowed on node
-
- /*!
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_node_is_online() instead
+ gboolean online; // Whether online
+
+ gboolean standby; // Whether in standby mode
+ gboolean standby_onfail; // Whether in standby mode due to on-fail
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_node_is_pending() instead
+ gboolean pending; // Whether controller membership is pending
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call !pcmk_node_is_clean() instead
+ gboolean unclean; // Whether node requires fencing
+
+ gboolean unseen; // Whether node has never joined cluster
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_node_is_shutting_down() instead
+ gboolean shutdown; // Whether shutting down
+
+ gboolean expected_up; // Whether expected join state is member
+ gboolean is_dc; // Whether node is cluster's DC
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_node_is_in_maintenance() instead
+ gboolean maintenance; // Whether in maintenance mode
+
+ gboolean rsc_discovery_enabled; // Whether probes are allowed on node
+
+ /*
* Whether this is a guest node whose guest resource must be recovered or a
* remote node that must be fenced
*/
gboolean remote_requires_reset;
- /*!
+ /*
* Whether this is a Pacemaker Remote node that was fenced since it was last
* connected by the cluster
*/
gboolean remote_was_fenced;
- /*!
+ /*
* Whether this is a Pacemaker Remote node previously marked in its
* node state as being in maintenance mode
*/
gboolean remote_maintenance;
- gboolean unpacked; //!< Whether node history has been unpacked
+ gboolean unpacked; // Whether node history has been unpacked
- /*!
+ /*
* Number of resources active on this node (valid after CIB status section
* has been unpacked, as long as pcmk_sched_no_counts was not set)
*/
int num_resources;
- //! Remote connection resource for node, if it is a Pacemaker Remote node
+ // Remote connection resource for node, if it is a Pacemaker Remote node
pcmk_resource_t *remote_rsc;
- GList *running_rsc; //!< List of resources active on node
- GList *allocated_rsc; //!< List of resources assigned to node
- GHashTable *attrs; //!< Node attributes
- GHashTable *utilization; //!< Node utilization attributes
- GHashTable *digest_cache; //!< Cache of calculated resource digests
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ // \deprecated Call pcmk_foreach_active_resource() instead
+ GList *running_rsc; // List of resources active on node
+
+ GList *allocated_rsc; // List of resources assigned to node
+ GHashTable *attrs; // Node attributes
+ GHashTable *utilization; // Node utilization attributes
+ GHashTable *digest_cache; // Cache of calculated resource digests
- /*!
+ /*
* Sum of priorities of all resources active on node and on any guest nodes
* connected to this node, with +1 for promoted instances (used to compare
- * nodes for priority-fencing-delay)
+ * nodes for PCMK_OPT_PRIORITY_FENCING_DELAY)
*/
int priority;
- pcmk_scheduler_t *data_set; //!< Cluster that node is part of
+ pcmk_scheduler_t *data_set; // Cluster that node is part of
};
+//!@}
-//! Implementation of pcmk_node_t
+// Implementation of pcmk_node_t
+// @COMPAT Make contents internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
struct pe_node_s {
- int weight; //!< Node score for a given resource
- gboolean fixed; //!< \deprecated Do not use
- int count; //!< Counter reused by assignment and promotion code
- struct pe_node_shared_s *details; //!< Basic node information
+ int weight; // Node score for a given resource
+ gboolean fixed; // \deprecated Do not use
+ int count; // Counter reused by assignment and promotion code
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ struct pe_node_shared_s *details; // Basic node information
// @COMPAT This should be enum pe_discover_e
- int rsc_discover_mode; //!< Probe mode (enum pe_discover_e)
+ int rsc_discover_mode; // Probe mode (enum pe_discover_e)
};
+//!@}
+
+bool pcmk_node_is_online(const pcmk_node_t *node);
+bool pcmk_node_is_pending(const pcmk_node_t *node);
+bool pcmk_node_is_clean(const pcmk_node_t *node);
+bool pcmk_node_is_shutting_down(const pcmk_node_t *node);
+bool pcmk_node_is_in_maintenance(const pcmk_node_t *node);
+
+bool pcmk_foreach_active_resource(pcmk_node_t *node,
+ bool (*fn)(pcmk_resource_t *, void *),
+ void *user_data);
+/*!
+ * \internal
+ * \brief Return a string suitable for logging as a node name
+ *
+ * \param[in] node Node to return a node name string for
+ *
+ * \return Node name if available, otherwise node ID if available,
+ * otherwise "unspecified node" if node is NULL or "unidentified node"
+ * if node has neither a name nor ID.
+ */
+static inline const char *
+pcmk__node_name(const pcmk_node_t *node)
+{
+ if (node == NULL) {
+ return "unspecified node";
+
+ } else if (node->details->uname != NULL) {
+ return node->details->uname;
+
+ } else if (node->details->id != NULL) {
+ return node->details->id;
+
+ } else {
+ return "unidentified node";
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether two node objects refer to the same node
+ *
+ * \param[in] node1 First node object to compare
+ * \param[in] node2 Second node object to compare
+ *
+ * \return true if \p node1 and \p node2 refer to the same node
+ */
+static inline bool
+pcmk__same_node(const pcmk_node_t *node1, const pcmk_node_t *node2)
+{
+ return (node1 != NULL) && (node2 != NULL)
+ && (node1->details == node2->details);
+}
#ifdef __cplusplus
}
diff --git a/include/crm/common/nodes_internal.h b/include/crm/common/nodes_internal.h
new file mode 100644
index 0000000..8f49566
--- /dev/null
+++ b/include/crm/common/nodes_internal.h
@@ -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 Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#ifndef PCMK__NODES_INTERNAL__H
+#define PCMK__NODES_INTERNAL__H
+
+/*
+ * Special node attributes
+ */
+
+#define PCMK__NODE_ATTR_SHUTDOWN "shutdown"
+
+/* @COMPAT Deprecated since 2.1.8. Use a location constraint with
+ * PCMK_XA_RSC_PATTERN=".*" and PCMK_XA_RESOURCE_DISCOVERY="never" instead of
+ * PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED="false".
+ */
+#define PCMK__NODE_ATTR_RESOURCE_DISCOVERY_ENABLED "resource-discovery-enabled"
+
+pcmk_node_t *pcmk__find_node_in_list(const GList *nodes, const char *node_name);
+
+#endif // PCMK__NODES_INTERNAL__H
diff --git a/include/crm/common/nvpair.h b/include/crm/common/nvpair.h
index 185bdc3..b68ba70 100644
--- a/include/crm/common/nvpair.h
+++ b/include/crm/common/nvpair.h
@@ -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.
*
@@ -8,17 +8,17 @@
*/
#ifndef PCMK__CRM_COMMON_NVPAIR__H
-# define PCMK__CRM_COMMON_NVPAIR__H
+#define PCMK__CRM_COMMON_NVPAIR__H
-# include <sys/time.h> // struct timeval
-# include <glib.h> // gpointer, gboolean, guint
-# include <libxml/tree.h> // xmlNode
-# include <crm/crm.h>
+#include <sys/time.h> // struct timeval
+#include <glib.h> // gpointer, gboolean, guint, GHashTable
+#include <libxml/tree.h> // xmlNode
+#include <crm/crm.h>
-# ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
-# endif
+#endif
/**
* \file
@@ -62,6 +62,9 @@ int crm_element_value_timeval(const xmlNode *data, const char *name_sec,
const char *name_usec, struct timeval *dest);
char *crm_element_value_copy(const xmlNode *data, const char *name);
+char *crm_meta_name(const char *field);
+const char *crm_meta_value(GHashTable *hash, const char *field);
+
/*!
* \brief Copy an element from one XML object to another
*
@@ -80,8 +83,8 @@ crm_copy_xml_element(const xmlNode *obj1, xmlNode *obj2, const char *element)
return value;
}
-# ifdef __cplusplus
+#ifdef __cplusplus
}
-# endif
+#endif
#endif // PCMK__CRM_COMMON_NVPAIR__H
diff --git a/include/crm/common/nvpair_internal.h b/include/crm/common/nvpair_internal.h
new file mode 100644
index 0000000..b771dfb
--- /dev/null
+++ b/include/crm/common/nvpair_internal.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_NVPAIR_INTERNAL__H
+#define PCMK__CRM_COMMON_NVPAIR_INTERNAL__H
+
+#include <glib.h> // gboolean
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/common/rules.h> // pcmk_rule_input_t
+#include <crm/common/iso8601.h> // crm_time_t
+#include <crm/common/strings_internal.h> // pcmk__str_eq(), etc.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Data needed to sort XML blocks of name/value pairs
+typedef struct unpack_data_s {
+ GHashTable *values; // Where to put name/value pairs
+ const char *first_id; // Block with this XML ID should sort first
+ pcmk_rule_input_t rule_input; // Data used to evaluate rules
+
+ // Whether each block's values should overwrite any existing ones
+ bool overwrite;
+
+ // If not NULL, this will be set to when rule evaluations will change next
+ crm_time_t *next_change;
+} pcmk__nvpair_unpack_t;
+
+/*!
+ * \internal
+ * \brief Insert a meta-attribute into a hash table
+ *
+ * \param[in] obj Resource (pe_resource_t) or action (pe_action_t) to add to
+ * \param[in] name Meta-attribute name
+ * \param[in] value Value to add
+ */
+#define pcmk__insert_meta(obj, name, value) do { \
+ 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)); \
+ } else if ((value) != NULL) { \
+ pcmk__insert_dup((obj)->meta, (name), (value)); \
+ } \
+ } while (0)
+
+int pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_NVPAIR_INTERNAL__H
diff --git a/include/crm/common/options.h b/include/crm/common/options.h
new file mode 100644
index 0000000..64cbf5e
--- /dev/null
+++ b/include/crm/common/options.h
@@ -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 Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#ifndef PCMK__CRM_COMMON_OPTIONS__H
+#define PCMK__CRM_COMMON_OPTIONS__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief API related to options
+ * \ingroup core
+ */
+
+/*
+ * Cluster options
+ */
+
+#define PCMK_OPT_BATCH_LIMIT "batch-limit"
+#define PCMK_OPT_CLUSTER_DELAY "cluster-delay"
+#define PCMK_OPT_CLUSTER_INFRASTRUCTURE "cluster-infrastructure"
+#define PCMK_OPT_CLUSTER_IPC_LIMIT "cluster-ipc-limit"
+#define PCMK_OPT_CLUSTER_NAME "cluster-name"
+#define PCMK_OPT_CLUSTER_RECHECK_INTERVAL "cluster-recheck-interval"
+#define PCMK_OPT_CONCURRENT_FENCING "concurrent-fencing"
+#define PCMK_OPT_DC_DEADTIME "dc-deadtime"
+#define PCMK_OPT_DC_VERSION "dc-version"
+#define PCMK_OPT_ELECTION_TIMEOUT "election-timeout"
+#define PCMK_OPT_ENABLE_ACL "enable-acl"
+#define PCMK_OPT_ENABLE_STARTUP_PROBES "enable-startup-probes"
+#define PCMK_OPT_FENCE_REACTION "fence-reaction"
+#define PCMK_OPT_HAVE_WATCHDOG "have-watchdog"
+#define PCMK_OPT_JOIN_FINALIZATION_TIMEOUT "join-finalization-timeout"
+#define PCMK_OPT_JOIN_INTEGRATION_TIMEOUT "join-integration-timeout"
+#define PCMK_OPT_LOAD_THRESHOLD "load-threshold"
+#define PCMK_OPT_MAINTENANCE_MODE "maintenance-mode"
+#define PCMK_OPT_MIGRATION_LIMIT "migration-limit"
+#define PCMK_OPT_NO_QUORUM_POLICY "no-quorum-policy"
+#define PCMK_OPT_NODE_ACTION_LIMIT "node-action-limit"
+#define PCMK_OPT_NODE_HEALTH_BASE "node-health-base"
+#define PCMK_OPT_NODE_HEALTH_GREEN "node-health-green"
+#define PCMK_OPT_NODE_HEALTH_RED "node-health-red"
+#define PCMK_OPT_NODE_HEALTH_STRATEGY "node-health-strategy"
+#define PCMK_OPT_NODE_HEALTH_YELLOW "node-health-yellow"
+#define PCMK_OPT_NODE_PENDING_TIMEOUT "node-pending-timeout"
+#define PCMK_OPT_PE_ERROR_SERIES_MAX "pe-error-series-max"
+#define PCMK_OPT_PE_INPUT_SERIES_MAX "pe-input-series-max"
+#define PCMK_OPT_PE_WARN_SERIES_MAX "pe-warn-series-max"
+#define PCMK_OPT_PLACEMENT_STRATEGY "placement-strategy"
+#define PCMK_OPT_PRIORITY_FENCING_DELAY "priority-fencing-delay"
+#define PCMK_OPT_SHUTDOWN_ESCALATION "shutdown-escalation"
+#define PCMK_OPT_SHUTDOWN_LOCK "shutdown-lock"
+#define PCMK_OPT_SHUTDOWN_LOCK_LIMIT "shutdown-lock-limit"
+#define PCMK_OPT_START_FAILURE_IS_FATAL "start-failure-is-fatal"
+#define PCMK_OPT_STARTUP_FENCING "startup-fencing"
+#define PCMK_OPT_STONITH_ACTION "stonith-action"
+#define PCMK_OPT_STONITH_ENABLED "stonith-enabled"
+#define PCMK_OPT_STONITH_MAX_ATTEMPTS "stonith-max-attempts"
+#define PCMK_OPT_STONITH_TIMEOUT "stonith-timeout"
+#define PCMK_OPT_STONITH_WATCHDOG_TIMEOUT "stonith-watchdog-timeout"
+#define PCMK_OPT_STOP_ALL_RESOURCES "stop-all-resources"
+#define PCMK_OPT_STOP_ORPHAN_ACTIONS "stop-orphan-actions"
+#define PCMK_OPT_STOP_ORPHAN_RESOURCES "stop-orphan-resources"
+#define PCMK_OPT_SYMMETRIC_CLUSTER "symmetric-cluster"
+#define PCMK_OPT_TRANSITION_DELAY "transition-delay"
+
+
+/*
+ * Meta-attributes
+ */
+
+#define PCMK_META_ALLOW_MIGRATE "allow-migrate"
+#define PCMK_META_ALLOW_UNHEALTHY_NODES "allow-unhealthy-nodes"
+#define PCMK_META_CLONE_MAX "clone-max"
+#define PCMK_META_CLONE_MIN "clone-min"
+#define PCMK_META_CLONE_NODE_MAX "clone-node-max"
+#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET "container-attribute-target"
+#define PCMK_META_CRITICAL "critical"
+#define PCMK_META_ENABLED "enabled"
+#define PCMK_META_FAILURE_TIMEOUT "failure-timeout"
+#define PCMK_META_GLOBALLY_UNIQUE "globally-unique"
+#define PCMK_META_INTERLEAVE "interleave"
+#define PCMK_META_INTERVAL "interval"
+#define PCMK_META_IS_MANAGED "is-managed"
+#define PCMK_META_INTERVAL_ORIGIN "interval-origin"
+#define PCMK_META_MAINTENANCE "maintenance"
+#define PCMK_META_MIGRATION_THRESHOLD "migration-threshold"
+#define PCMK_META_MULTIPLE_ACTIVE "multiple-active"
+#define PCMK_META_NOTIFY "notify"
+#define PCMK_META_ON_FAIL "on-fail"
+#define PCMK_META_ORDERED "ordered"
+#define PCMK_META_PRIORITY "priority"
+#define PCMK_META_PROMOTABLE "promotable"
+#define PCMK_META_PROMOTED_MAX "promoted-max"
+#define PCMK_META_PROMOTED_NODE_MAX "promoted-node-max"
+#define PCMK_META_RECORD_PENDING "record-pending"
+#define PCMK_META_REMOTE_ADDR "remote-addr"
+#define PCMK_META_REMOTE_ALLOW_MIGRATE "remote-allow-migrate"
+#define PCMK_META_REMOTE_CONNECT_TIMEOUT "remote-connect-timeout"
+#define PCMK_META_REMOTE_NODE "remote-node"
+#define PCMK_META_REMOTE_PORT "remote-port"
+#define PCMK_META_REQUIRES "requires"
+#define PCMK_META_RESOURCE_STICKINESS "resource-stickiness"
+#define PCMK_META_START_DELAY "start-delay"
+#define PCMK_META_TARGET_ROLE "target-role"
+#define PCMK_META_TIMEOUT "timeout"
+#define PCMK_META_TIMESTAMP_FORMAT "timestamp-format"
+
+
+/*
+ * Remote resource instance attributes
+ */
+
+#define PCMK_REMOTE_RA_ADDR "addr"
+#define PCMK_REMOTE_RA_PORT "port"
+#define PCMK_REMOTE_RA_RECONNECT_INTERVAL "reconnect_interval"
+#define PCMK_REMOTE_RA_SERVER "server"
+
+
+/*
+ * Enumerated values
+ */
+
+#define PCMK_VALUE_ALWAYS "always"
+#define PCMK_VALUE_AND "and"
+#define PCMK_VALUE_BALANCED "balanced"
+#define PCMK_VALUE_BLOCK "block"
+#define PCMK_VALUE_BOOLEAN "boolean"
+#define PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS "cib-bootstrap-options"
+#define PCMK_VALUE_COROSYNC "corosync"
+#define PCMK_VALUE_CREATE "create"
+#define PCMK_VALUE_CUSTOM "custom"
+#define PCMK_VALUE_DATE_SPEC "date_spec"
+#define PCMK_VALUE_DEFAULT "default"
+#define PCMK_VALUE_DEFINED "defined"
+#define PCMK_VALUE_DELETE "delete"
+#define PCMK_VALUE_DEMOTE "demote"
+#define PCMK_VALUE_DENY "deny"
+#define PCMK_VALUE_DURATION "duration"
+#define PCMK_VALUE_DYNAMIC_LIST "dynamic-list"
+#define PCMK_VALUE_EQ "eq"
+#define PCMK_VALUE_EXCLUSIVE "exclusive"
+#define PCMK_VALUE_FAILED "failed"
+#define PCMK_VALUE_FALSE "false"
+#define PCMK_VALUE_FENCE "fence"
+#define PCMK_VALUE_FENCING "fencing"
+#define PCMK_VALUE_FREEZE "freeze"
+#define PCMK_VALUE_GRANTED "granted"
+#define PCMK_VALUE_GREEN "green"
+#define PCMK_VALUE_GT "gt"
+#define PCMK_VALUE_GTE "gte"
+#define PCMK_VALUE_HOST "host"
+#define PCMK_VALUE_IGNORE "ignore"
+#define PCMK_VALUE_IN_RANGE "in_range"
+#define PCMK_VALUE_INFINITY "INFINITY"
+#define PCMK_VALUE_INTEGER "integer"
+#define PCMK_VALUE_LITERAL "literal"
+#define PCMK_VALUE_LT "lt"
+#define PCMK_VALUE_LTE "lte"
+#define PCMK_VALUE_MANDATORY "Mandatory"
+#define PCMK_VALUE_MEMBER "member"
+#define PCMK_VALUE_META "meta"
+#define PCMK_VALUE_MIGRATE_ON_RED "migrate-on-red"
+#define PCMK_VALUE_MINIMAL "minimal"
+#define PCMK_VALUE_MINUS_INFINITY "-" PCMK_VALUE_INFINITY
+#define PCMK_VALUE_MODIFY "modify"
+#define PCMK_VALUE_MOVE "move"
+#define PCMK_VALUE_NE "ne"
+#define PCMK_VALUE_NEVER "never"
+#define PCMK_VALUE_NONE "none"
+#define PCMK_VALUE_NONNEGATIVE_INTEGER "nonnegative_integer"
+#define PCMK_VALUE_NOT_DEFINED "not_defined"
+#define PCMK_VALUE_NOTHING "nothing"
+#define PCMK_VALUE_NUMBER "number"
+#define PCMK_VALUE_OFFLINE "offline"
+#define PCMK_VALUE_ONLINE "online"
+#define PCMK_VALUE_ONLY_GREEN "only-green"
+#define PCMK_VALUE_OPTIONAL "Optional"
+#define PCMK_VALUE_OR "or"
+#define PCMK_VALUE_PANIC "panic"
+#define PCMK_VALUE_PARAM "param"
+#define PCMK_VALUE_PENDING "pending"
+#define PCMK_VALUE_PERCENTAGE "percentage"
+#define PCMK_VALUE_PLUS_INFINITY "+" PCMK_VALUE_INFINITY
+#define PCMK_VALUE_PORT "port"
+#define PCMK_VALUE_PROGRESSIVE "progressive"
+#define PCMK_VALUE_QUORUM "quorum"
+#define PCMK_VALUE_READ "read"
+#define PCMK_VALUE_RED "red"
+#define PCMK_VALUE_REMOTE "remote"
+#define PCMK_VALUE_RESTART "restart"
+#define PCMK_VALUE_RESTART_CONTAINER "restart-container"
+#define PCMK_VALUE_REVOKED "revoked"
+#define PCMK_VALUE_SCORE "score"
+#define PCMK_VALUE_SELECT "select"
+#define PCMK_VALUE_SERIALIZE "Serialize"
+#define PCMK_VALUE_STANDBY "standby"
+#define PCMK_VALUE_STATIC_LIST "static-list"
+#define PCMK_VALUE_STATUS "status"
+#define PCMK_VALUE_STRING "string"
+#define PCMK_VALUE_STOP "stop"
+#define PCMK_VALUE_STOP_ONLY "stop_only"
+#define PCMK_VALUE_STOP_START "stop_start"
+#define PCMK_VALUE_STOP_UNEXPECTED "stop_unexpected"
+#define PCMK_VALUE_SUCCESS "success"
+#define PCMK_VALUE_TIMEOUT "timeout"
+#define PCMK_VALUE_TRUE "true"
+#define PCMK_VALUE_UNFENCING "unfencing"
+#define PCMK_VALUE_UNKNOWN "unknown"
+#define PCMK_VALUE_UTILIZATION "utilization"
+#define PCMK_VALUE_VERSION "version"
+#define PCMK_VALUE_WRITE "write"
+#define PCMK_VALUE_YELLOW "yellow"
+
+// @COMPAT This will become a deprecated alias for PCMK_VALUE_FENCE (see T279)
+#define PCMK_VALUE_FENCE_LEGACY "suicide"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_OPTIONS__H
diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h
index 5c561fd..92506a0 100644
--- a/include/crm/common/options_internal.h
+++ b/include/crm/common/options_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the Pacemaker project contributors
+ * Copyright 2006-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,15 +8,17 @@
*/
#ifndef PCMK__OPTIONS_INTERNAL__H
-# define PCMK__OPTIONS_INTERNAL__H
+#define PCMK__OPTIONS_INTERNAL__H
-# ifndef PCMK__CONFIG_H
-# define PCMK__CONFIG_H
-# include <config.h> // _Noreturn
-# endif
+#ifndef PCMK__CONFIG_H
+#define PCMK__CONFIG_H
+#include <config.h> // _Noreturn
+#endif
-# include <glib.h> // GHashTable
-# include <stdbool.h> // bool
+#include <glib.h> // GHashTable
+#include <stdbool.h> // bool
+
+#include <crm/common/util.h> // pcmk_parse_interval_spec()
_Noreturn void pcmk__cli_help(char cmd);
@@ -34,6 +36,42 @@ bool pcmk__env_option_enabled(const char *daemon, const char *option);
* Cluster option handling
*/
+/*!
+ * \internal
+ * \enum pcmk__opt_flags
+ * \brief Option flags
+ */
+enum pcmk__opt_flags {
+ pcmk__opt_none = 0U, //!< No additional information
+
+ /*!
+ * \brief In CIB manager metadata
+ *
+ * \deprecated This flag will be removed with CIB manager metadata
+ */
+ pcmk__opt_based = (1U << 0),
+
+ /*!
+ * \brief In controller metadata
+ *
+ * \deprecated This flag will be removed with controller metadata
+ */
+ pcmk__opt_controld = (1U << 1),
+
+ /*!
+ * \brief In scheduler metadata
+ *
+ * \deprecated This flag will be removed with scheduler metadata
+ */
+ pcmk__opt_schedulerd = (1U << 2),
+
+ pcmk__opt_advanced = (1U << 3), //!< Advanced use only
+ pcmk__opt_generated = (1U << 4), //!< Generated by Pacemaker
+ pcmk__opt_deprecated = (1U << 5), //!< Option is deprecated
+ pcmk__opt_fencing = (1U << 6), //!< Common fencing resource parameter
+ pcmk__opt_primitive = (1U << 7), //!< Primitive resource meta-attribute
+};
+
typedef struct pcmk__cluster_option_s {
const char *name;
const char *alt_name;
@@ -43,37 +81,44 @@ typedef struct pcmk__cluster_option_s {
bool (*is_valid)(const char *);
+ uint32_t flags; //!< Group of <tt>enum pcmk__opt_flags</tt>
+
const char *description_short;
const char *description_long;
} pcmk__cluster_option_t;
-const char *pcmk__cluster_option(GHashTable *options,
- const pcmk__cluster_option_t *option_list,
- int len, const char *name);
+const char *pcmk__cluster_option(GHashTable *options, const char *name);
-gchar *pcmk__format_option_metadata(const char *name, const char *desc_short,
- const char *desc_long,
- pcmk__cluster_option_t *option_list,
- int len);
+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);
+int pcmk__output_fencing_params(pcmk__output_t *out, const char *name,
+ const char *desc_short, const char *desc_long,
+ bool all);
+int pcmk__output_primitive_meta(pcmk__output_t *out, const char *name,
+ const char *desc_short, const char *desc_long,
+ bool all);
-void pcmk__validate_cluster_options(GHashTable *options,
- pcmk__cluster_option_t *option_list,
- int len);
+int pcmk__daemon_metadata(pcmk__output_t *out, const char *name,
+ const char *short_desc, const char *long_desc,
+ enum pcmk__opt_flags filter);
+
+void pcmk__validate_cluster_options(GHashTable *options);
bool pcmk__valid_interval_spec(const char *value);
bool pcmk__valid_boolean(const char *value);
-bool pcmk__valid_number(const char *value);
-bool pcmk__valid_positive_number(const char *value);
-bool pcmk__valid_quorum(const char *value);
-bool pcmk__valid_script(const char *value);
+bool pcmk__valid_int(const char *value);
+bool pcmk__valid_positive_int(const char *value);
+bool pcmk__valid_no_quorum_policy(const char *value);
bool pcmk__valid_percentage(const char *value);
+bool pcmk__valid_placement_strategy(const char *value);
// from watchdog.c
-long pcmk__get_sbd_timeout(void);
+long pcmk__get_sbd_watchdog_timeout(void);
bool pcmk__get_sbd_sync_resource_startup(void);
-long pcmk__auto_watchdog_timeout(void);
-bool pcmk__valid_sbd_timeout(const char *value);
+long pcmk__auto_stonith_watchdog_timeout(void);
+bool pcmk__valid_stonith_watchdog_timeout(const char *value);
// Constants for environment variable names
#define PCMK__ENV_AUTHKEY_LOCATION "authkey_location"
@@ -93,8 +138,8 @@ bool pcmk__valid_sbd_timeout(const char *value);
#define PCMK__ENV_NODE_ACTION_LIMIT "node_action_limit"
#define PCMK__ENV_NODE_START_STATE "node_start_state"
#define PCMK__ENV_PANIC_ACTION "panic_action"
-#define PCMK__ENV_PHYSICAL_HOST "physical_host"
#define PCMK__ENV_REMOTE_ADDRESS "remote_address"
+#define PCMK__ENV_REMOTE_SCHEMA_DIRECTORY "remote_schema_directory"
#define PCMK__ENV_REMOTE_PID1 "remote_pid1"
#define PCMK__ENV_REMOTE_PORT "remote_port"
#define PCMK__ENV_RESPAWNED "respawned"
@@ -123,30 +168,97 @@ bool pcmk__valid_sbd_timeout(const char *value);
*/
#define PCMK__ENV_SHUTDOWN_DELAY "shutdown_delay"
-// Constants for cluster option names
-#define PCMK__OPT_NODE_HEALTH_BASE "node-health-base"
-#define PCMK__OPT_NODE_HEALTH_GREEN "node-health-green"
-#define PCMK__OPT_NODE_HEALTH_RED "node-health-red"
-#define PCMK__OPT_NODE_HEALTH_STRATEGY "node-health-strategy"
-#define PCMK__OPT_NODE_HEALTH_YELLOW "node-health-yellow"
+// @COMPAT Deprecated since 2.1.0
+#define PCMK__OPT_REMOVE_AFTER_STOP "remove-after-stop"
// Constants for meta-attribute names
-#define PCMK__META_ALLOW_UNHEALTHY_NODES "allow-unhealthy-nodes"
+#define PCMK__META_CLONE "clone"
+#define PCMK__META_CONTAINER "container"
+#define PCMK__META_DIGESTS_ALL "digests-all"
+#define PCMK__META_DIGESTS_SECURE "digests-secure"
+#define PCMK__META_INTERNAL_RSC "internal_rsc"
+#define PCMK__META_MIGRATE_SOURCE "migrate_source"
+#define PCMK__META_MIGRATE_TARGET "migrate_target"
+#define PCMK__META_ON_NODE "on_node"
+#define PCMK__META_ON_NODE_UUID "on_node_uuid"
+#define PCMK__META_OP_NO_WAIT "op_no_wait"
+#define PCMK__META_OP_TARGET_RC "op_target_rc"
+#define PCMK__META_PHYSICAL_HOST "physical-host"
+#define PCMK__META_STONITH_ACTION "stonith_action"
-// Constants for enumerated values for various options
+/* @TODO Plug these in. Currently, they're never set. These are op attrs for use
+ * with https://projects.clusterlabs.org/T382.
+ */
+#define PCMK__META_CLEAR_FAILURE_OP "clear_failure_op"
+#define PCMK__META_CLEAR_FAILURE_INTERVAL "clear_failure_interval"
+
+// @COMPAT Deprecated meta-attribute since 2.1.0
+#define PCMK__META_CAN_FAIL "can_fail"
+
+// @COMPAT Deprecated alias for PCMK__META_PROMOTED_MAX since 2.0.0
+#define PCMK__META_PROMOTED_MAX_LEGACY "master-max"
+
+// @COMPAT Deprecated alias for PCMK__META_PROMOTED_NODE_MAX since 2.0.0
+#define PCMK__META_PROMOTED_NODE_MAX_LEGACY "master-node-max"
+
+// @COMPAT Deprecated meta-attribute since 2.0.0
+#define PCMK__META_RESTART_TYPE "restart-type"
+
+// @COMPAT Deprecated meta-attribute since 2.0.0
+#define PCMK__META_ROLE_AFTER_FAILURE "role_after_failure"
+
+// Constants for enumerated values
+#define PCMK__VALUE_ATTRD "attrd"
+#define PCMK__VALUE_BOLD "bold"
+#define PCMK__VALUE_BROADCAST "broadcast"
+#define PCMK__VALUE_CIB "cib"
+#define PCMK__VALUE_CIB_DIFF_NOTIFY "cib_diff_notify"
+#define PCMK__VALUE_CIB_NOTIFY "cib_notify"
+#define PCMK__VALUE_CIB_POST_NOTIFY "cib_post_notify"
+#define PCMK__VALUE_CIB_PRE_NOTIFY "cib_pre_notify"
+#define PCMK__VALUE_CIB_UPDATE_CONFIRMATION "cib_update_confirmation"
#define PCMK__VALUE_CLUSTER "cluster"
-#define PCMK__VALUE_CUSTOM "custom"
-#define PCMK__VALUE_FENCING "fencing"
-#define PCMK__VALUE_GREEN "green"
+#define PCMK__VALUE_CRMD "crmd"
+#define PCMK__VALUE_EN "en"
+#define PCMK__VALUE_EPOCH "epoch"
+#define PCMK__VALUE_HEALTH_RED "health_red"
+#define PCMK__VALUE_HEALTH_YELLOW "health_yellow"
+#define PCMK__VALUE_INIT "init"
#define PCMK__VALUE_LOCAL "local"
-#define PCMK__VALUE_MIGRATE_ON_RED "migrate-on-red"
-#define PCMK__VALUE_NONE "none"
-#define PCMK__VALUE_NOTHING "nothing"
-#define PCMK__VALUE_ONLY_GREEN "only-green"
-#define PCMK__VALUE_PROGRESSIVE "progressive"
-#define PCMK__VALUE_QUORUM "quorum"
-#define PCMK__VALUE_RED "red"
-#define PCMK__VALUE_UNFENCING "unfencing"
-#define PCMK__VALUE_YELLOW "yellow"
+#define PCMK__VALUE_LRMD "lrmd"
+#define PCMK__VALUE_MAINT "maint"
+#define PCMK__VALUE_OUTPUT "output"
+#define PCMK__VALUE_PASSWORD "password"
+#define PCMK__VALUE_PING "ping"
+#define PCMK__VALUE_PRIMITIVE "primitive"
+#define PCMK__VALUE_REFRESH "refresh"
+#define PCMK__VALUE_REQUEST "request"
+#define PCMK__VALUE_RESPONSE "response"
+#define PCMK__VALUE_RSC_FAILED "rsc-failed"
+#define PCMK__VALUE_RSC_FAILURE_IGNORED "rsc-failure-ignored"
+#define PCMK__VALUE_RSC_MANAGED "rsc-managed"
+#define PCMK__VALUE_RSC_MULTIPLE "rsc-multiple"
+#define PCMK__VALUE_RSC_OK "rsc-ok"
+#define PCMK__VALUE_RUNNING "running"
+#define PCMK__VALUE_SHUTDOWN_COMPLETE "shutdown_complete"
+#define PCMK__VALUE_SHUTTING_DOWN "shutting_down"
+#define PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE "st-async-timeout-value"
+#define PCMK__VALUE_ST_NOTIFY "st_notify"
+#define PCMK__VALUE_ST_NOTIFY_DISCONNECT "st_notify_disconnect"
+#define PCMK__VALUE_ST_NOTIFY_FENCE "st_notify_fence"
+#define PCMK__VALUE_ST_NOTIFY_HISTORY "st_notify_history"
+#define PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED "st_notify_history_synced"
+#define PCMK__VALUE_STARTING_DAEMONS "starting_daemons"
+#define PCMK__VALUE_STONITH_NG "stonith-ng"
+#define PCMK__VALUE_WAIT_FOR_PING "wait_for_ping"
+#define PCMK__VALUE_WARNING "warning"
+
+/* @COMPAT Deprecated since 2.1.7 (used with PCMK__XA_ORDERING attribute of
+ * resource sets)
+ */
+#define PCMK__VALUE_GROUP "group"
+
+// @COMPAT Drop when daemon metadata commands are dropped
+#define PCMK__VALUE_TIME "time"
#endif // PCMK__OPTIONS_INTERNAL__H
diff --git a/include/crm/common/output.h b/include/crm/common/output.h
index 112ebcb..acb0a0e 100644
--- a/include/crm/common/output.h
+++ b/include/crm/common/output.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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_OUTPUT__H
-# define PCMK__CRM_COMMON_OUTPUT__H
+#define PCMK__CRM_COMMON_OUTPUT__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
index 274bd85..79efef9 100644
--- a/include/crm/common/output_internal.h
+++ b/include/crm/common/output_internal.h
@@ -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.
*
@@ -8,16 +8,16 @@
*/
#ifndef PCMK__OUTPUT_INTERNAL__H
-# define PCMK__OUTPUT_INTERNAL__H
+#define PCMK__OUTPUT_INTERNAL__H
-# include <stdbool.h>
-# include <stdint.h>
-# include <stdio.h>
-# include <libxml/tree.h>
-# include <libxml/HTMLtree.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <libxml/tree.h>
+#include <libxml/HTMLtree.h>
-# include <glib.h>
-# include <crm/common/results.h>
+#include <glib.h>
+#include <crm/common/results.h>
#ifdef __cplusplus
extern "C" {
@@ -29,9 +29,9 @@ extern "C" {
*/
#if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
-# define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+#define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
#else
-# define PCMK__OUTPUT_ARGS(ARGS...)
+#define PCMK__OUTPUT_ARGS(ARGS...)
#endif
typedef struct pcmk__output_s pcmk__output_t;
@@ -143,10 +143,7 @@ typedef struct pcmk__supported_format_s {
*/
extern GOptionEntry pcmk__html_output_entries[];
-extern GOptionEntry pcmk__log_output_entries[];
-extern GOptionEntry pcmk__none_output_entries[];
extern GOptionEntry pcmk__text_output_entries[];
-extern GOptionEntry pcmk__xml_output_entries[];
pcmk__output_t *pcmk__mk_html_output(char **argv);
pcmk__output_t *pcmk__mk_log_output(char **argv);
@@ -155,11 +152,10 @@ pcmk__output_t *pcmk__mk_text_output(char **argv);
pcmk__output_t *pcmk__mk_xml_output(char **argv);
#define PCMK__SUPPORTED_FORMAT_HTML { "html", pcmk__mk_html_output, pcmk__html_output_entries }
-#define PCMK__SUPPORTED_FORMAT_LOG { "log", pcmk__mk_log_output, pcmk__log_output_entries }
-#define PCMK__SUPPORTED_FORMAT_NONE { PCMK__VALUE_NONE, pcmk__mk_none_output, \
- pcmk__none_output_entries }
+#define PCMK__SUPPORTED_FORMAT_LOG { "log", pcmk__mk_log_output, NULL }
+#define PCMK__SUPPORTED_FORMAT_NONE { PCMK_VALUE_NONE, pcmk__mk_none_output, NULL }
#define PCMK__SUPPORTED_FORMAT_TEXT { "text", pcmk__mk_text_output, pcmk__text_output_entries }
-#define PCMK__SUPPORTED_FORMAT_XML { "xml", pcmk__mk_xml_output, pcmk__xml_output_entries }
+#define PCMK__SUPPORTED_FORMAT_XML { "xml", pcmk__mk_xml_output, NULL }
/*!
* \brief This structure contains everything that makes up a single output
@@ -658,6 +654,8 @@ pcmk__register_messages(pcmk__output_t *out,
/* Functions that are useful for implementing custom message formatters */
+void pcmk__output_text_set_fancy(pcmk__output_t *out, bool enabled);
+
/*!
* \internal
* \brief A printf-like function.
@@ -737,29 +735,9 @@ pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) G
void
pcmk__text_prompt(const char *prompt, bool echo, char **dest);
-/*!
- * \internal
- * \brief Get the log level used by the formatted output logger
- *
- * \param[in] out Output object
- *
- * \return Log level used by \p out
- */
uint8_t
pcmk__output_get_log_level(const pcmk__output_t *out);
-/*!
- * \internal
- * \brief Set the log level used by the formatted output logger.
- *
- * \param[in,out] out The output functions structure.
- * \param[in] log_level The log level constant (LOG_INFO, LOG_ERR, etc.)
- * to use.
- *
- * \note By default, LOG_INFO is used.
- * \note Almost all formatted output messages will respect this setting.
- * However, out->err will always log at LOG_ERR.
- */
void
pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level);
@@ -887,16 +865,23 @@ xmlNodePtr
pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
const char *class_name, const char *text);
+xmlNode *pcmk__html_create(xmlNode *parent, const char *name, const char *id,
+ const char *class);
+
/*!
* \internal
* \brief Add an HTML tag to the <head> section.
*
* The arguments after name are a NULL-terminated list of keys and values,
* all of which will be added as attributes to the given tag. For instance,
- * the following code would generate the tag "<meta http-equiv='refresh' content='19'>":
+ * the following code would generate the tag
+ * "<meta http-equiv='refresh' content='19'>":
*
* \code
- * pcmk__html_add_header("meta", "http-equiv", "refresh", "content", "19", NULL);
+ * pcmk__html_add_header(PCMK__XE_META,
+ * PCMK__XA_HTTP_EQUIV, PCMK__VALUE_REFRESH,
+ * PCMK__XA_CONTENT, "19",
+ * NULL);
* \endcode
*
* \param[in] name The HTML tag for the new node.
@@ -918,12 +903,52 @@ G_GNUC_NULL_TERMINATED;
void pcmk__output_and_clear_error(GError **error, pcmk__output_t *out);
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml);
-void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml);
+void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml);
int pcmk__log_output_new(pcmk__output_t **out);
int pcmk__text_output_new(pcmk__output_t **out, const char *filename);
/*!
* \internal
+ * \brief Check whether older style XML output is enabled
+ *
+ * The legacy flag should be used sparingly. Its meaning depends on the context
+ * in which it's used.
+ *
+ * \param[in] out Output object
+ *
+ * \return \c true if the \c legacy_xml flag is enabled for \p out, or \c false
+ * otherwise
+ */
+// @COMPAT This can be removed when `crm_mon -X` and daemon metadata are removed
+bool pcmk__output_get_legacy_xml(pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Enable older style XML output
+ *
+ * The legacy flag should be used sparingly. Its meaning depends on the context
+ * in which it's used.
+ *
+ * \param[in,out] out Output object
+ */
+// @COMPAT This can be removed when `crm_mon -X` and daemon metadata are removed
+void pcmk__output_set_legacy_xml(pcmk__output_t *out);
+
+/*!
+ * \internal
+ * \brief Enable using the <list> element for lists
+ *
+ * \note This function is only used in limited places and should not be
+ * used anywhere new. We are trying to discourage and ultimately remove
+ * uses of this style of list.
+ *
+ * @COMPAT This can be removed when the stonith_admin and crm_resource
+ * schemas can be changed
+ */
+void pcmk__output_enable_list_element(pcmk__output_t *out);
+
+/*!
+ * \internal
* \brief Select an updated return code for an operation on a \p pcmk__output_t
*
* This function helps to keep an up-to-date record of the most relevant return
diff --git a/include/crm/common/primitive_internal.h b/include/crm/common/primitive_internal.h
new file mode 100644
index 0000000..394495e
--- /dev/null
+++ b/include/crm/common/primitive_internal.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_PRIMITIVE_INTERNAL__H
+#define PCMK__CRM_COMMON_PRIMITIVE_INTERNAL__H
+
+#include <stdbool.h> // bool
+#include <crm/common/scheduler_types.h> // pcmk_resource_t
+#include <crm/common/resources.h> // pcmk_rsc_variant_primitive
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \internal
+ * \brief Check whether a resource is a primitive resource
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return true if \p rsc is a primitive, otherwise false
+ */
+static inline bool
+pcmk__is_primitive(const pcmk_resource_t *rsc)
+{
+ return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_PRIMITIVE_INTERNAL__H
diff --git a/include/crm/common/remote_internal.h b/include/crm/common/remote_internal.h
index 030c7a4..d55f25f 100644
--- a/include/crm/common/remote_internal.h
+++ b/include/crm/common/remote_internal.h
@@ -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.
*
@@ -7,8 +7,13 @@
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
-#ifndef PCMK__REMOTE_INTERNAL__H
-# define PCMK__REMOTE_INTERNAL__H
+#ifndef PCMK__CRM_COMMON_REMOTE_INTERNAL__H
+#define PCMK__CRM_COMMON_REMOTE_INTERNAL__H
+
+#include <stdbool.h> // bool
+
+#include <crm/common/nodes.h> // pcmk_node_variant_remote
+#include <crm/common/scheduler_types.h> // pcmk_node_t
// internal functions from remote.c
@@ -24,8 +29,54 @@ int pcmk__connect_remote(const char *host, int port, int timeout_ms,
int pcmk__accept_remote_connection(int ssock, int *csock);
void pcmk__sockaddr2str(const void *sa, char *s);
-# ifdef HAVE_GNUTLS_GNUTLS_H
-# include <gnutls/gnutls.h>
+/*!
+ * \internal
+ * \brief Check whether a node is a Pacemaker Remote node of any kind
+ *
+ * \param[in] node Node to check
+ *
+ * \return true if \p node is a remote, guest, or bundle node, otherwise false
+ */
+static inline bool
+pcmk__is_pacemaker_remote_node(const pcmk_node_t *node)
+{
+ return (node != NULL) && (node->details->type == pcmk_node_variant_remote);
+}
+
+/*!
+ * \internal
+ * \brief Check whether a node is a remote node
+ *
+ * \param[in] node Node to check
+ *
+ * \return true if \p node is a remote node, otherwise false
+ */
+static inline bool
+pcmk__is_remote_node(const pcmk_node_t *node)
+{
+ return pcmk__is_pacemaker_remote_node(node)
+ && ((node->details->remote_rsc == NULL)
+ || (node->details->remote_rsc->container == NULL));
+}
+
+/*!
+ * \internal
+ * \brief Check whether a node is a guest or bundle node
+ *
+ * \param[in] node Node to check
+ *
+ * \return true if \p node is a guest or bundle node, otherwise false
+ */
+static inline bool
+pcmk__is_guest_or_bundle_node(const pcmk_node_t *node)
+{
+ return pcmk__is_pacemaker_remote_node(node)
+ && (node->details->remote_rsc != NULL)
+ && (node->details->remote_rsc->container != NULL);
+}
+
+#ifdef HAVE_GNUTLS_GNUTLS_H
+#include <gnutls/gnutls.h>
gnutls_session_t *pcmk__new_tls_session(int csock, unsigned int conn_type,
gnutls_credentials_type_t cred_type,
@@ -44,5 +95,5 @@ int pcmk__read_handshake_data(const pcmk__client_t *client);
*/
int pcmk__tls_client_handshake(pcmk__remote_t *remote, int timeout_ms);
-# endif // HAVE_GNUTLS_GNUTLS_H
-#endif // PCMK__REMOTE_INTERNAL__H
+#endif // HAVE_GNUTLS_GNUTLS_H
+#endif // PCMK__CRM_COMMON_REMOTE_INTERNAL__H
diff --git a/include/crm/common/resources.h b/include/crm/common/resources.h
index 043dc1c..9b38e68 100644
--- a/include/crm/common/resources.h
+++ b/include/crm/common/resources.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,8 +8,9 @@
*/
#ifndef PCMK__CRM_COMMON_RESOURCES__H
-# define PCMK__CRM_COMMON_RESOURCES__H
+#define PCMK__CRM_COMMON_RESOURCES__H
+#include <stdbool.h> // bool
#include <sys/types.h> // time_t
#include <libxml/tree.h> // xmlNode
#include <glib.h> // gboolean, guint, GList, GHashTable
@@ -27,171 +28,156 @@ extern "C" {
* \ingroup core
*/
-//! Resource variants supported by Pacemaker
+// Resource variants supported by Pacemaker
+//!@{
+//! \deprecated Do not use
enum pe_obj_types {
// Order matters: some code compares greater or lesser than
- pcmk_rsc_variant_unknown = -1, //!< Unknown resource variant
- pcmk_rsc_variant_primitive = 0, //!< Primitive resource
- pcmk_rsc_variant_group = 1, //!< Group resource
- pcmk_rsc_variant_clone = 2, //!< Clone resource
- pcmk_rsc_variant_bundle = 3, //!< Bundle resource
+ pcmk_rsc_variant_unknown = -1, // Unknown resource variant
+ pcmk_rsc_variant_primitive = 0, // Primitive resource
+ pcmk_rsc_variant_group = 1, // Group resource
+ pcmk_rsc_variant_clone = 2, // Clone resource
+ pcmk_rsc_variant_bundle = 3, // Bundle resource
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_rsc_variant_unknown instead
pe_unknown = pcmk_rsc_variant_unknown,
-
- //! \deprecated Use pcmk_rsc_variant_primitive instead
pe_native = pcmk_rsc_variant_primitive,
-
- //! \deprecated Use pcmk_rsc_variant_group instead
pe_group = pcmk_rsc_variant_group,
-
- //! \deprecated Use pcmk_rsc_variant_clone instead
pe_clone = pcmk_rsc_variant_clone,
-
- //! \deprecated Use pcmk_rsc_variant_bundle instead
pe_container = pcmk_rsc_variant_bundle,
#endif
};
-//! What resource needs before it can be recovered from a failed node
+// What resource needs before it can be recovered from a failed node
enum rsc_start_requirement {
- pcmk_requires_nothing = 0, //!< Resource can be recovered immediately
- pcmk_requires_quorum = 1, //!< Resource can be recovered if quorate
- pcmk_requires_fencing = 2, //!< Resource can be recovered after fencing
+ pcmk_requires_nothing = 0, // Resource can be recovered immediately
+ pcmk_requires_quorum = 1, // Resource can be recovered if quorate
+ pcmk_requires_fencing = 2, // Resource can be recovered after fencing
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_requires_nothing instead
rsc_req_nothing = pcmk_requires_nothing,
-
- //! \deprecated Use pcmk_requires_quorum instead
rsc_req_quorum = pcmk_requires_quorum,
-
- //! \deprecated Use pcmk_requires_fencing instead
rsc_req_stonith = pcmk_requires_fencing,
#endif
};
-//! How to recover a resource that is incorrectly active on multiple nodes
+// How to recover a resource that is incorrectly active on multiple nodes
enum rsc_recovery_type {
- pcmk_multiply_active_restart = 0, //!< Stop on all, start on desired
- pcmk_multiply_active_stop = 1, //!< Stop on all and leave stopped
- pcmk_multiply_active_block = 2, //!< Do nothing to resource
- pcmk_multiply_active_unexpected = 3, //!< Stop unexpected instances
+ pcmk_multiply_active_restart = 0, // Stop on all, start on desired
+ pcmk_multiply_active_stop = 1, // Stop on all and leave stopped
+ pcmk_multiply_active_block = 2, // Do nothing to resource
+ pcmk_multiply_active_unexpected = 3, // Stop unexpected instances
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Use pcmk_multiply_active_restart instead
recovery_stop_start = pcmk_multiply_active_restart,
-
- //! \deprecated Use pcmk_multiply_active_stop instead
recovery_stop_only = pcmk_multiply_active_stop,
-
- //! \deprecated Use pcmk_multiply_active_block instead
recovery_block = pcmk_multiply_active_block,
-
- //! \deprecated Use pcmk_multiply_active_unexpected instead
recovery_stop_unexpected = pcmk_multiply_active_unexpected,
#endif
};
-//! Resource scheduling flags
+// Resource scheduling flags
enum pcmk_rsc_flags {
- //! No resource flags set (compare with equality rather than bit set)
+ // No resource flags set (compare with equality rather than bit set)
pcmk_no_rsc_flags = 0ULL,
- //! Whether resource has been removed from the configuration
+ // Whether resource has been removed from the configuration
pcmk_rsc_removed = (1ULL << 0),
- //! Whether resource is managed
+ // Whether resource is managed
pcmk_rsc_managed = (1ULL << 1),
- //! Whether resource is blocked from further action
+ // Whether resource is blocked from further action
pcmk_rsc_blocked = (1ULL << 2),
- //! Whether resource has been removed but has a container
+ // Whether resource has been removed but has a container
pcmk_rsc_removed_filler = (1ULL << 3),
- //! Whether resource has clone notifications enabled
+ // Whether resource has clone notifications enabled
pcmk_rsc_notify = (1ULL << 4),
- //! Whether resource is not an anonymous clone instance
+ // Whether resource is not an anonymous clone instance
pcmk_rsc_unique = (1ULL << 5),
- //! Whether resource's class is "stonith"
+ // Whether resource's class is "stonith"
pcmk_rsc_fence_device = (1ULL << 6),
- //! Whether resource can be promoted and demoted
+ // Whether resource can be promoted and demoted
pcmk_rsc_promotable = (1ULL << 7),
- //! Whether resource has not yet been assigned to a node
+ // Whether resource has not yet been assigned to a node
pcmk_rsc_unassigned = (1ULL << 8),
- //! Whether resource is in the process of being assigned to a node
+ // Whether resource is in the process of being assigned to a node
pcmk_rsc_assigning = (1ULL << 9),
- //! Whether resource is in the process of modifying allowed node scores
+ // Whether resource is in the process of modifying allowed node scores
pcmk_rsc_updating_nodes = (1ULL << 10),
- //! Whether resource is in the process of scheduling actions to restart
+ // Whether resource is in the process of scheduling actions to restart
pcmk_rsc_restarting = (1ULL << 11),
- //! Whether resource must be stopped (instead of demoted) if it is failed
+ // Whether resource must be stopped (instead of demoted) if it is failed
pcmk_rsc_stop_if_failed = (1ULL << 12),
- //! Whether a reload action has been scheduled for resource
+ // Whether a reload action has been scheduled for resource
pcmk_rsc_reload = (1ULL << 13),
- //! Whether resource is a remote connection allowed to run on a remote node
+ // Whether resource is a remote connection allowed to run on a remote node
pcmk_rsc_remote_nesting_allowed = (1ULL << 14),
- //! Whether resource has "critical" meta-attribute enabled
+ // Whether resource has \c PCMK_META_CRITICAL meta-attribute enabled
pcmk_rsc_critical = (1ULL << 15),
- //! Whether resource is considered failed
+ // Whether resource is considered failed
pcmk_rsc_failed = (1ULL << 16),
- //! Flag for non-scheduler code to use to detect recursion loops
+ // Flag for non-scheduler code to use to detect recursion loops
pcmk_rsc_detect_loop = (1ULL << 17),
- //! \deprecated Do not use
+ // \deprecated Do not use
pcmk_rsc_runnable = (1ULL << 18),
- //! Whether resource has pending start action in history
+ // Whether resource has pending start action in history
pcmk_rsc_start_pending = (1ULL << 19),
- //! \deprecated Do not use
+ // \deprecated Do not use
pcmk_rsc_starting = (1ULL << 20),
- //! \deprecated Do not use
+ // \deprecated Do not use
pcmk_rsc_stopping = (1ULL << 21),
- //! Whether resource is multiply active with recovery set to stop_unexpected
+ /*
+ * Whether resource is multiply active with recovery set to
+ * \c PCMK_VALUE_STOP_UNEXPECTED
+ */
pcmk_rsc_stop_unexpected = (1ULL << 22),
- //! Whether resource is allowed to live-migrate
+ // Whether resource is allowed to live-migrate
pcmk_rsc_migratable = (1ULL << 23),
- //! Whether resource has an ignorable failure
+ // Whether resource has an ignorable failure
pcmk_rsc_ignore_failure = (1ULL << 24),
- //! Whether resource is an implicit container resource for a bundle replica
+ // Whether resource is an implicit container resource for a bundle replica
pcmk_rsc_replica_container = (1ULL << 25),
- //! Whether resource, its node, or entire cluster is in maintenance mode
+ // Whether resource, its node, or entire cluster is in maintenance mode
pcmk_rsc_maintenance = (1ULL << 26),
- //! \deprecated Do not use
+ // \deprecated Do not use
pcmk_rsc_has_filler = (1ULL << 27),
- //! Whether resource can be started or promoted only on quorate nodes
+ // Whether resource can be started or promoted only on quorate nodes
pcmk_rsc_needs_quorum = (1ULL << 28),
- //! Whether resource requires fencing before recovery if on unclean node
+ // Whether resource requires fencing before recovery if on unclean node
pcmk_rsc_needs_fencing = (1ULL << 29),
- //! Whether resource can be started or promoted only on unfenced nodes
+ // Whether resource can be started or promoted only on unfenced nodes
pcmk_rsc_needs_unfencing = (1ULL << 30),
};
+//!@}
//! Search options for resources (exact resource ID always matches)
enum pe_find {
@@ -259,12 +245,15 @@ enum pe_print_options {
//!@}
// Resource assignment methods (implementation defined by libpacemaker)
-//! This type should be considered internal to Pacemaker
+//! \deprecated Do not use (public access will be removed in a future release)
typedef struct resource_alloc_functions_s pcmk_assignment_methods_t;
-//! Resource object methods
+// Resource object methods
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
typedef struct resource_object_functions_s {
- /*!
+ /*
* \brief Parse variant-specific resource XML from CIB into struct members
*
* \param[in,out] rsc Partially unpacked resource
@@ -274,7 +263,7 @@ typedef struct resource_object_functions_s {
*/
gboolean (*unpack)(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler);
- /*!
+ /*
* \brief Search for a resource ID in a resource and its children
*
* \param[in] rsc Search this resource and its children
@@ -287,7 +276,7 @@ typedef struct resource_object_functions_s {
pcmk_resource_t *(*find_rsc)(pcmk_resource_t *rsc, const char *search,
const pcmk_node_t *node, int flags);
- /*!
+ /*
* \brief Get value of a resource instance attribute
*
* \param[in,out] rsc Resource to check
@@ -302,11 +291,11 @@ typedef struct resource_object_functions_s {
char *(*parameter)(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create,
const char *name, pcmk_scheduler_t *scheduler);
- //! \deprecated Do not use
+ // \deprecated Do not use
void (*print)(pcmk_resource_t *rsc, const char *pre_text, long options,
void *print_data);
- /*!
+ /*
* \brief Check whether a resource is active
*
* \param[in] rsc Resource to check
@@ -316,7 +305,7 @@ typedef struct resource_object_functions_s {
*/
gboolean (*active)(pcmk_resource_t *rsc, gboolean all);
- /*!
+ /*
* \brief Get resource's current or assigned role
*
* \param[in] rsc Resource to check
@@ -326,7 +315,7 @@ typedef struct resource_object_functions_s {
*/
enum rsc_role_e (*state)(const pcmk_resource_t *rsc, gboolean current);
- /*!
+ /*
* \brief List nodes where a resource (or any of its children) is
*
* \param[in] rsc Resource to check
@@ -339,14 +328,14 @@ typedef struct resource_object_functions_s {
pcmk_node_t *(*location)(const pcmk_resource_t *rsc, GList **list,
int current);
- /*!
+ /*
* \brief Free all memory used by a resource
*
* \param[in,out] rsc Resource to free
*/
void (*free)(pcmk_resource_t *rsc);
- /*!
+ /*
* \brief Increment cluster's instance counts for a resource
*
* Given a resource, increment its cluster's ninstances, disabled_resources,
@@ -356,7 +345,7 @@ typedef struct resource_object_functions_s {
*/
void (*count)(pcmk_resource_t *rsc);
- /*!
+ /*
* \brief Check whether a given resource is in a list of resources
*
* \param[in] rsc Resource ID to check for
@@ -369,7 +358,7 @@ typedef struct resource_object_functions_s {
gboolean (*is_filtered)(const pcmk_resource_t *rsc, GList *only_rsc,
gboolean check_parent);
- /*!
+ /*
* \brief Find a node (and optionally count all) where resource is active
*
* \param[in] rsc Resource to check
@@ -378,14 +367,14 @@ typedef struct resource_object_functions_s {
*
* \return A node where the resource is active, preferring the source node
* if the resource is involved in a partial migration, or a clean,
- * online node if the resource's "requires" is "quorum" or
- * "nothing", otherwise NULL.
+ * online node if the resource's \c PCMK_META_REQUIRES is
+ * \c PCMK_VALUE_QUORUM or \c PCMK_VALUE_NOTHING, otherwise \c NULL.
*/
pcmk_node_t *(*active_node)(const pcmk_resource_t *rsc,
unsigned int *count_all,
unsigned int *count_clean);
- /*!
+ /*
* \brief Get maximum resource instances per node
*
* \param[in] rsc Resource to check
@@ -394,43 +383,53 @@ typedef struct resource_object_functions_s {
*/
unsigned int (*max_per_node)(const pcmk_resource_t *rsc);
} pcmk_rsc_methods_t;
+//!@}
-//! Implementation of pcmk_resource_t
+// Implementation of pcmk_resource_t
+// @COMPAT Make this internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
struct pe_resource_s {
- char *id; //!< Resource ID in configuration
- char *clone_name; //!< Resource instance ID in history
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_resource_id() instead
+ char *id; // Resource ID in configuration
- //! Resource configuration (possibly expanded from template)
+ char *clone_name; // Resource instance ID in history
+
+ // Resource configuration (possibly expanded from template)
xmlNode *xml;
- //! Original resource configuration, if using template
+ // Original resource configuration, if using template
xmlNode *orig_xml;
- //! Configuration of resource operations (possibly expanded from template)
+ // Configuration of resource operations (possibly expanded from template)
xmlNode *ops_xml;
- pcmk_scheduler_t *cluster; //!< Cluster that resource is part of
- pcmk_resource_t *parent; //!< Resource's parent resource, if any
- enum pe_obj_types variant; //!< Resource variant
- void *variant_opaque; //!< Variant-specific (and private) data
- pcmk_rsc_methods_t *fns; //!< Resource object methods
- pcmk_assignment_methods_t *cmds; //!< Resource assignment methods
-
- enum rsc_recovery_type recovery_type; //!< How to recover if failed
-
- enum pe_restart restart_type; //!< \deprecated Do not use
- int priority; //!< Configured priority
- int stickiness; //!< Extra preference for current node
- int sort_index; //!< Promotion score on assigned node
- int failure_timeout; //!< Failure timeout
- int migration_threshold; //!< Migration threshold
- guint remote_reconnect_ms; //!< Retry interval for remote connections
- char *pending_task; //!< Pending action in history, if any
- unsigned long long flags; //!< Group of enum pcmk_rsc_flags
+ pcmk_scheduler_t *cluster; // Cluster that resource is part of
+ pcmk_resource_t *parent; // Resource's parent resource, if any
+ enum pe_obj_types variant; // Resource variant
+ void *variant_opaque; // Variant-specific (and private) data
+ pcmk_rsc_methods_t *fns; // Resource object methods
+ pcmk_assignment_methods_t *cmds; // Resource assignment methods
+
+ enum rsc_recovery_type recovery_type; // How to recover if failed
+
+ enum pe_restart restart_type; // \deprecated Do not use
+ int priority; // Configured priority
+ int stickiness; // Extra preference for current node
+ int sort_index; // Promotion score on assigned node
+ int failure_timeout; // Failure timeout
+ int migration_threshold; // Migration threshold
+ guint remote_reconnect_ms; // Retry interval for remote connections
+ char *pending_task; // Pending action in history, if any
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_resource_is_managed() instead
+ unsigned long long flags; // Group of enum pcmk_rsc_flags
// @TODO Merge these into flags
- gboolean is_remote_node; //!< Whether this is a remote connection
- gboolean exclusive_discover; //!< Whether exclusive probing is enabled
+ gboolean is_remote_node; // Whether this is a remote connection
+ gboolean exclusive_discover; // Whether exclusive probing is enabled
/* Pay special attention to whether you want to use rsc_cons_lhs and
* rsc_cons directly, which include only colocations explicitly involving
@@ -439,54 +438,51 @@ struct pe_resource_s {
* colocations involving the resource's ancestors as well.
*/
- //!@{
- //! This field should be treated as internal to Pacemaker
GList *rsc_cons_lhs; // Colocations of other resources with this one
GList *rsc_cons; // Colocations of this resource with others
GList *rsc_location; // Location constraints for resource
GList *actions; // Actions scheduled for resource
GList *rsc_tickets; // Ticket constraints for resource
- //!@}
- pcmk_node_t *allocated_to; //!< Node resource is assigned to
+ pcmk_node_t *allocated_to; // Node resource is assigned to
- //! The destination node, if migrate_to completed but migrate_from has not
+ // The destination node, if migrate_to completed but migrate_from has not
pcmk_node_t *partial_migration_target;
- //! The source node, if migrate_to completed but migrate_from has not
+ // The source node, if migrate_to completed but migrate_from has not
pcmk_node_t *partial_migration_source;
- //! Nodes where resource may be active
+ // Nodes where resource may be active
GList *running_on;
- //! Nodes where resource has been probed (key is node ID, not name)
+ // Nodes where resource has been probed (key is node ID, not name)
GHashTable *known_on;
- //! Nodes where resource may run (key is node ID, not name)
+ // Nodes where resource may run (key is node ID, not name)
GHashTable *allowed_nodes;
- enum rsc_role_e role; //!< Resource's current role
- enum rsc_role_e next_role; //!< Resource's scheduled next role
+ enum rsc_role_e role; // Resource's current role
+ enum rsc_role_e next_role; // Resource's scheduled next role
- GHashTable *meta; //!< Resource's meta-attributes
- GHashTable *parameters; //!< \deprecated Use pe_rsc_params() instead
- GHashTable *utilization; //!< Resource's utilization attributes
+ GHashTable *meta; // Resource's meta-attributes
+ GHashTable *parameters; // \deprecated Use pe_rsc_params() instead
+ GHashTable *utilization; // Resource's utilization attributes
- GList *children; //!< Resource's child resources, if any
+ GList *children; // Resource's child resources, if any
// Source nodes where stop is needed after migrate_from and migrate_to
GList *dangling_migrations;
- pcmk_resource_t *container; //!< Resource containing this one, if any
- GList *fillers; //!< Resources contained by this one, if any
+ pcmk_resource_t *container; // Resource containing this one, if any
+ GList *fillers; // Resources contained by this one, if any
// @COMPAT These should be made const at next API compatibility break
- pcmk_node_t *pending_node; //!< Node on which pending_task is happening
- pcmk_node_t *lock_node; //!< Resource shutdown-locked to this node
+ pcmk_node_t *pending_node; // Node on which pending_task is happening
+ pcmk_node_t *lock_node; // Resource shutdown-locked to this node
- time_t lock_time; //!< When shutdown lock started
+ time_t lock_time; // When shutdown lock started
- /*!
+ /*
* Resource parameters may have node-attribute-based rules, which means the
* values can vary by node. This table has node names as keys and parameter
* name/value tables as values. Use pe_rsc_params() to get the table for a
@@ -494,6 +490,10 @@ struct pe_resource_s {
*/
GHashTable *parameter_cache;
};
+//!@}
+
+const char *pcmk_resource_id(const pcmk_resource_t *rsc);
+bool pcmk_resource_is_managed(const pcmk_resource_t *rsc);
#ifdef __cplusplus
}
diff --git a/include/crm/common/resources_internal.h b/include/crm/common/resources_internal.h
new file mode 100644
index 0000000..42e88af
--- /dev/null
+++ b/include/crm/common/resources_internal.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_RESOURCES_INTERNAL__H
+#define PCMK__CRM_COMMON_RESOURCES_INTERNAL__H
+
+#include <crm/common/resources.h> // enum rsc_recovery_type
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *pcmk__multiply_active_text(enum rsc_recovery_type recovery);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_RESOURCES_INTERNAL__H
diff --git a/include/crm/common/results.h b/include/crm/common/results.h
index 87d00d2..a2d35dd 100644
--- a/include/crm/common/results.h
+++ b/include/crm/common/results.h
@@ -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.
*
@@ -7,7 +7,7 @@
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__CRM_COMMON_RESULTS__H
-# define PCMK__CRM_COMMON_RESULTS__H
+#define PCMK__CRM_COMMON_RESULTS__H
#ifdef __cplusplus
extern "C" {
@@ -39,10 +39,9 @@ extern "C" {
# endif
#endif
-# define CRM_ASSERT(expr) do { \
+#define CRM_ASSERT(expr) do { \
if (!(expr)) { \
crm_abort(__FILE__, __func__, __LINE__, #expr, TRUE, FALSE); \
- abort(); /* crm_abort() doesn't always abort! */ \
} \
} while(0)
@@ -65,31 +64,39 @@ extern "C" {
*/
// Legacy custom return codes for Pacemaker API functions (deprecated)
-# define pcmk_ok 0
-# define PCMK_ERROR_OFFSET 190 /* Replacements on non-linux systems, see include/portability.h */
-# define PCMK_CUSTOM_OFFSET 200 /* Purely custom codes */
-# define pcmk_err_generic 201
-# define pcmk_err_no_quorum 202
-# define pcmk_err_schema_validation 203
-# define pcmk_err_transform_failed 204
-# define pcmk_err_old_data 205
-# define pcmk_err_diff_failed 206
-# define pcmk_err_diff_resync 207
-# define pcmk_err_cib_modified 208
-# define pcmk_err_cib_backup 209
-# define pcmk_err_cib_save 210
-# define pcmk_err_schema_unchanged 211
-# define pcmk_err_cib_corrupt 212
-# define pcmk_err_multiple 213
-# define pcmk_err_node_unknown 214
-# define pcmk_err_already 215
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define pcmk_ok 0
+
+#define PCMK_ERROR_OFFSET 190 /* Replacements on non-linux systems, see include/portability.h */
+#define PCMK_CUSTOM_OFFSET 200 /* Purely custom codes */
+#define pcmk_err_generic 201
+#define pcmk_err_no_quorum 202
+#define pcmk_err_schema_validation 203
+#define pcmk_err_transform_failed 204
+#define pcmk_err_old_data 205
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define pcmk_err_diff_failed 206
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+#define pcmk_err_diff_resync 207
+
+#define pcmk_err_cib_modified 208
+#define pcmk_err_cib_backup 209
+#define pcmk_err_cib_save 210
+#define pcmk_err_schema_unchanged 211
+#define pcmk_err_cib_corrupt 212
+#define pcmk_err_multiple 213
+#define pcmk_err_node_unknown 214
+#define pcmk_err_already 215
/* On HPPA 215 is ENOSYM (Unknown error 215), which hopefully never happens. */
#ifdef __hppa__
-# define pcmk_err_bad_nvpair 250 /* 216 is ENOTSOCK */
-# define pcmk_err_unknown_format 252 /* 217 is EDESTADDRREQ */
+#define pcmk_err_bad_nvpair 250 /* 216 is ENOTSOCK */
+#define pcmk_err_unknown_format 252 /* 217 is EDESTADDRREQ */
#else
-# define pcmk_err_bad_nvpair 216
-# define pcmk_err_unknown_format 217
+#define pcmk_err_bad_nvpair 216
+#define pcmk_err_unknown_format 217
#endif
/*!
@@ -151,6 +158,7 @@ enum pcmk_rc_e {
// Values -1 through -1000 reserved for caller use
+ // NOTE: sbd (as of at least 1.5.2) uses this
pcmk_rc_ok = 0
// Positive values reserved for system error numbers
@@ -168,13 +176,19 @@ enum pcmk_rc_e {
*/
enum ocf_exitcode {
PCMK_OCF_OK = 0, //!< Success
+
+ // NOTE: booth (as of at least 1.1) uses this value
PCMK_OCF_UNKNOWN_ERROR = 1, //!< Unspecified error
+
PCMK_OCF_INVALID_PARAM = 2, //!< Parameter invalid (in local context)
PCMK_OCF_UNIMPLEMENT_FEATURE = 3, //!< Requested action not implemented
PCMK_OCF_INSUFFICIENT_PRIV = 4, //!< Insufficient privileges
PCMK_OCF_NOT_INSTALLED = 5, //!< Dependencies not available locally
PCMK_OCF_NOT_CONFIGURED = 6, //!< Parameter invalid (inherently)
+
+ // NOTE: booth (as of at least 1.1) uses this value
PCMK_OCF_NOT_RUNNING = 7, //!< Service safely stopped
+
PCMK_OCF_RUNNING_PROMOTED = 8, //!< Service active and promoted
PCMK_OCF_FAILED_PROMOTED = 9, //!< Service failed and possibly in promoted role
PCMK_OCF_DEGRADED = 190, //!< Service active but more likely to fail soon
@@ -209,6 +223,7 @@ enum ocf_exitcode {
#endif
};
+// NOTE: sbd (as of at least 1.5.2) uses this
/*!
* \enum crm_exit_e
* \brief Exit status codes for tools and daemons
@@ -356,15 +371,24 @@ enum pcmk_result_type {
int pcmk_result_get_strings(int code, enum pcmk_result_type type,
const char **name, const char **desc);
const char *pcmk_rc_name(int rc);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
const char *pcmk_rc_str(int rc);
+
crm_exit_t pcmk_rc2exitc(int rc);
enum ocf_exitcode pcmk_rc2ocf(int rc);
int pcmk_rc2legacy(int rc);
int pcmk_legacy2rc(int legacy_rc);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
const char *pcmk_strerror(int rc);
+
const char *pcmk_errorname(int rc);
const char *crm_exit_name(crm_exit_t exit_code);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
const char *crm_exit_str(crm_exit_t exit_code);
+
_Noreturn crm_exit_t crm_exit(crm_exit_t rc);
static inline const char *
diff --git a/include/crm/common/results_compat.h b/include/crm/common/results_compat.h
index 278e48e..31fc8f2 100644
--- a/include/crm/common/results_compat.h
+++ b/include/crm/common/results_compat.h
@@ -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.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_RESULTS_COMPAT__H
-# define PCMK__CRM_COMMON_RESULTS_COMPAT__H
+#define PCMK__CRM_COMMON_RESULTS_COMPAT__H
#include <crm/common/results.h>
diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h
index 09907e9..c2a3f60 100644
--- a/include/crm/common/results_internal.h
+++ b/include/crm/common/results_internal.h
@@ -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.
*
@@ -12,13 +12,26 @@
#include <glib.h> // GQuark
-// Generic result code type
+extern const size_t pcmk__n_rc;
int pcmk__result_bounds(enum pcmk_result_type, int *lower, int *upper);
-// Standard Pacemaker API return codes
-
-extern const size_t pcmk__n_rc;
+/*!
+ * \internal
+ * \brief Abort without dumping core if a pointer is \c NULL
+ *
+ * This is intended to check for memory allocation failure, rather than for null
+ * pointers in general.
+ *
+ * \param[in] ptr Pointer to check
+ */
+#define pcmk__mem_assert(ptr) do { \
+ if ((ptr) == NULL) { \
+ crm_abort(__FILE__, __func__, __LINE__, "Out of memory", FALSE, \
+ TRUE); \
+ crm_exit(CRM_EX_OSERR); \
+ } \
+ } while (0)
/* Error domains for use with g_set_error */
diff --git a/include/crm/common/roles.h b/include/crm/common/roles.h
index 1498097..e315d6b 100644
--- a/include/crm/common/roles.h
+++ b/include/crm/common/roles.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_ROLES__H
-# define PCMK__CRM_COMMON_ROLES__H
+#define PCMK__CRM_COMMON_ROLES__H
#ifdef __cplusplus
extern "C" {
@@ -20,6 +20,13 @@ extern "C" {
* \ingroup core
*/
+// String equivalents of enum rsc_role_e
+
+#define PCMK_ROLE_STOPPED "Stopped"
+#define PCMK_ROLE_STARTED "Started"
+#define PCMK_ROLE_UNPROMOTED "Unpromoted"
+#define PCMK_ROLE_PROMOTED "Promoted"
+
/*!
* Possible roles that a resource can be in
* (order matters; values can be compared with less than and greater than)
@@ -55,6 +62,9 @@ enum rsc_role_e {
#endif
};
+const char *pcmk_role_text(enum rsc_role_e role);
+enum rsc_role_e pcmk_parse_role(const char *role);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/roles_internal.h b/include/crm/common/roles_internal.h
index e304f13..7df71c6 100644
--- a/include/crm/common/roles_internal.h
+++ b/include/crm/common/roles_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_ROLES_INTERNAL__H
-# define PCMK__CRM_COMMON_ROLES_INTERNAL__H
+#define PCMK__CRM_COMMON_ROLES_INTERNAL__H
#ifdef __cplusplus
extern "C" {
@@ -16,13 +16,49 @@ extern "C" {
// String equivalents of enum rsc_role_e
#define PCMK__ROLE_UNKNOWN "Unknown"
-#define PCMK__ROLE_STOPPED "Stopped"
-#define PCMK__ROLE_STARTED "Started"
-#define PCMK__ROLE_UNPROMOTED "Unpromoted"
-#define PCMK__ROLE_PROMOTED "Promoted"
#define PCMK__ROLE_UNPROMOTED_LEGACY "Slave"
#define PCMK__ROLE_PROMOTED_LEGACY "Master"
+/*!
+ * \internal
+ * \brief Set resource flags
+ *
+ * \param[in,out] resource Resource to set flags for
+ * \param[in] flags_to_set Group of enum pcmk_rsc_flags to set
+ */
+#define pcmk__set_rsc_flags(resource, flags_to_set) do { \
+ (resource)->flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \
+ (flags_to_set), #flags_to_set); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Clear resource flags
+ *
+ * \param[in,out] resource Resource to clear flags for
+ * \param[in] flags_to_clear Group of enum pcmk_rsc_flags to clear
+ */
+#define pcmk__clear_rsc_flags(resource, flags_to_clear) do { \
+ (resource)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \
+ (flags_to_clear), #flags_to_clear); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Get node where resource is currently active (if any)
+ *
+ * \param[in] rsc Resource to check
+ *
+ * \return Node that \p rsc is active on, if any, otherwise NULL
+ */
+static inline pcmk_node_t *
+pcmk__current_node(const pcmk_resource_t *rsc)
+{
+ return (rsc == NULL)? NULL : rsc->fns->active_node(rsc, NULL, NULL);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/rules.h b/include/crm/common/rules.h
new file mode 100644
index 0000000..ad4e94f
--- /dev/null
+++ b/include/crm/common/rules.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_RULES__H
+#define PCMK__CRM_COMMON_RULES__H
+
+#include <glib.h> // guint, GHashTable
+#include <regex.h> // regmatch_t
+#include <libxml/tree.h> // xmlNode
+#include <crm/common/iso8601.h> // crm_time_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \file
+ * \brief Scheduler API for rules
+ * \ingroup core
+ */
+
+/* Allowed subexpressions of a rule
+ * @COMPAT This should be made internal at an API compatibility break
+ */
+//!@{
+//! \deprecated For Pacemaker use only
+enum expression_type {
+ pcmk__condition_unknown = 0, // Unknown or invalid condition
+ pcmk__condition_rule = 1, // Nested rule
+ pcmk__condition_attribute = 2, // Node attribute expression
+ pcmk__condition_location = 3, // Node location expression
+ pcmk__condition_datetime = 5, // Date/time expression
+ pcmk__condition_resource = 7, // Resource agent expression
+ pcmk__condition_operation = 8, // Operation expression
+
+#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+ not_expr = pcmk__condition_unknown,
+ nested_rule = pcmk__condition_rule,
+ attr_expr = pcmk__condition_attribute,
+ loc_expr = pcmk__condition_location,
+ role_expr = 4,
+ time_expr = pcmk__condition_datetime,
+ version_expr = 6,
+ rsc_expr = pcmk__condition_resource,
+ op_expr = pcmk__condition_operation,
+#endif
+};
+//!@}
+
+//! Data used to evaluate a rule (any NULL items are ignored)
+typedef struct pcmk_rule_input {
+ // Used to evaluate date expressions
+ const crm_time_t *now; //!< Current time for rule evaluation purposes
+
+ // Used to evaluate resource type expressions
+ const char *rsc_standard; //!< Resource standard that rule applies to
+ const char *rsc_provider; //!< Resource provider that rule applies to
+ const char *rsc_agent; //!< Resource agent that rule applies to
+
+ // Used to evaluate operation type expressions
+ const char *op_name; //! Operation name that rule applies to
+ guint op_interval_ms; //! Operation interval that rule applies to
+
+ // Remaining members are used to evaluate node attribute expressions
+
+ /*!
+ * Node attributes for rule evaluation purposes
+ *
+ * \note Though not const, this is used only with g_hash_table_lookup().
+ */
+ GHashTable *node_attrs;
+
+ // Remaining members are used only within location constraint rules
+
+ /*!
+ * Resource parameters that can be used as the reference value source
+ *
+ * \note Though not const, this is used only with g_hash_table_lookup().
+ */
+ GHashTable *rsc_params;
+
+ /*!
+ * Resource meta-attributes that can be used as the reference value source
+ *
+ * \note Though not const, this is used only with g_hash_table_lookup().
+ */
+ GHashTable *rsc_meta;
+
+ //! Resource ID to compare against a location constraint's resource pattern
+ const char *rsc_id;
+
+ //! Resource pattern submatches (as set by regexec()) for rsc_id
+ const regmatch_t *rsc_id_submatches;
+
+ //! Number of entries in rsc_id_submatches
+ int rsc_id_nmatches;
+} pcmk_rule_input_t;
+
+int pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input,
+ crm_time_t *next_change);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_RULES__H
diff --git a/include/crm/common/rules_internal.h b/include/crm/common/rules_internal.h
new file mode 100644
index 0000000..5fed3f7
--- /dev/null
+++ b/include/crm/common/rules_internal.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_RULES_INTERNAL__H
+#define PCMK__CRM_COMMON_RULES_INTERNAL__H
+
+#include <regex.h> // regmatch_t
+#include <libxml/tree.h> // xmlNode
+
+#include <crm/common/rules.h> // enum expression_type, etc.
+#include <crm/common/iso8601.h> // crm_time_t
+
+enum pcmk__combine {
+ pcmk__combine_unknown,
+ pcmk__combine_and,
+ pcmk__combine_or,
+};
+
+enum expression_type pcmk__condition_type(const xmlNode *condition);
+char *pcmk__replace_submatches(const char *string, const char *match,
+ const regmatch_t submatches[], int nmatches);
+enum pcmk__combine pcmk__parse_combine(const char *combine);
+
+int pcmk__evaluate_date_expression(const xmlNode *date_expression,
+ const crm_time_t *now,
+ crm_time_t *next_change);
+int pcmk__evaluate_condition(xmlNode *expr, const pcmk_rule_input_t *rule_input,
+ crm_time_t *next_change);
+int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input,
+ crm_time_t *next_change);
+
+#endif // PCMK__CRM_COMMON_RULES_INTERNAL__H
diff --git a/include/crm/common/scheduler.h b/include/crm/common/scheduler.h
index 96f9a62..f75d808 100644
--- a/include/crm/common/scheduler.h
+++ b/include/crm/common/scheduler.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_SCHEDULER__H
-# define PCMK__CRM_COMMON_SCHEDULER__H
+#define PCMK__CRM_COMMON_SCHEDULER__H
#include <sys/types.h> // time_t
#include <libxml/tree.h> // xmlNode
@@ -20,6 +20,7 @@
#include <crm/common/nodes.h>
#include <crm/common/resources.h>
#include <crm/common/roles.h>
+#include <crm/common/rules.h>
#include <crm/common/scheduler_types.h>
#include <crm/common/tags.h>
#include <crm/common/tickets.h>
@@ -34,6 +35,7 @@ extern "C" {
* \ingroup core
*/
+// NOTE: sbd (as of at least 1.5.2) uses this enum
//! Possible responses to loss of quorum
enum pe_quorum_policy {
pcmk_no_quorum_freeze, //<! Do not recover resources from outside partition
@@ -43,193 +45,250 @@ enum pe_quorum_policy {
pcmk_no_quorum_demote, //<! Demote promotable resources and stop all others
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+ // NOTE: sbd (as of at least 1.5.2) uses this value
//! \deprecated Use pcmk_no_quorum_freeze instead
no_quorum_freeze = pcmk_no_quorum_freeze,
+ // NOTE: sbd (as of at least 1.5.2) uses this value
//! \deprecated Use pcmk_no_quorum_stop instead
no_quorum_stop = pcmk_no_quorum_stop,
+ // NOTE: sbd (as of at least 1.5.2) uses this value
//! \deprecated Use pcmk_no_quorum_ignore instead
no_quorum_ignore = pcmk_no_quorum_ignore,
//! \deprecated Use pcmk_no_quorum_fence instead
no_quorum_suicide = pcmk_no_quorum_fence,
+ // NOTE: sbd (as of at least 1.5.2) uses this value
//! \deprecated Use pcmk_no_quorum_demote instead
no_quorum_demote = pcmk_no_quorum_demote,
#endif
};
-//! Scheduling options and conditions
+// Scheduling options and conditions
+//!@{
+//! \deprecated Do not use
enum pcmk_scheduler_flags {
- //! No scheduler flags set (compare with equality rather than bit set)
+ // No scheduler flags set (compare with equality rather than bit set)
pcmk_sched_none = 0ULL,
- // These flags are dynamically determined conditions
+ /* These flags are dynamically determined conditions */
- //! Whether partition has quorum (via have-quorum property)
+ // Whether partition has quorum (via \c PCMK_XA_HAVE_QUORUM attribute)
+ //! \deprecated Call pcmk_has_quorum() to check quorum instead
pcmk_sched_quorate = (1ULL << 0),
- //! Whether cluster is symmetric (via symmetric-cluster property)
+ // Whether cluster is symmetric (via symmetric-cluster property)
pcmk_sched_symmetric_cluster = (1ULL << 1),
- //! Whether cluster is in maintenance mode (via maintenance-mode property)
+ // Whether cluster is in maintenance mode (via maintenance-mode property)
pcmk_sched_in_maintenance = (1ULL << 3),
- //! Whether fencing is enabled (via stonith-enabled property)
+ // Whether fencing is enabled (via stonith-enabled property)
pcmk_sched_fencing_enabled = (1ULL << 4),
- //! Whether cluster has a fencing resource (via CIB resources)
+ // Whether cluster has a fencing resource (via CIB resources)
+ /*! \deprecated To indicate the cluster has a fencing resource, add either a
+ * fencing resource configuration or the have-watchdog cluster option to the
+ * input CIB
+ */
pcmk_sched_have_fencing = (1ULL << 5),
- //! Whether any resource provides or requires unfencing (via CIB resources)
+ // Whether any resource provides or requires unfencing (via CIB resources)
pcmk_sched_enable_unfencing = (1ULL << 6),
- //! Whether concurrent fencing is allowed (via concurrent-fencing property)
+ // Whether concurrent fencing is allowed (via concurrent-fencing property)
pcmk_sched_concurrent_fencing = (1ULL << 7),
- /*!
+ /*
* Whether resources removed from the configuration should be stopped (via
* stop-orphan-resources property)
*/
pcmk_sched_stop_removed_resources = (1ULL << 8),
- /*!
+ /*
* Whether recurring actions removed from the configuration should be
* cancelled (via stop-orphan-actions property)
*/
pcmk_sched_cancel_removed_actions = (1ULL << 9),
- //! Whether to stop all resources (via stop-all-resources property)
+ // Whether to stop all resources (via stop-all-resources property)
pcmk_sched_stop_all = (1ULL << 10),
- /*!
- * Whether start failure should be treated as if migration-threshold is 1
- * (via start-failure-is-fatal property)
+ /*
+ * Whether start failure should be treated as if
+ * \c PCMK_META_MIGRATION_THRESHOLD is 1 (via
+ * \c PCMK_OPT_START_FAILURE_IS_FATAL property)
*/
pcmk_sched_start_failure_fatal = (1ULL << 12),
- //! \deprecated Do not use
+ // Unused
pcmk_sched_remove_after_stop = (1ULL << 13),
- //! Whether unseen nodes should be fenced (via startup-fencing property)
+ // Whether unseen nodes should be fenced (via startup-fencing property)
pcmk_sched_startup_fencing = (1ULL << 14),
- /*!
+ /*
* Whether resources should be left stopped when their node shuts down
* cleanly (via shutdown-lock property)
*/
pcmk_sched_shutdown_lock = (1ULL << 15),
- /*!
+ /*
* Whether resources' current state should be probed (when unknown) before
* scheduling any other actions (via the enable-startup-probes property)
*/
pcmk_sched_probe_resources = (1ULL << 16),
- //! Whether the CIB status section has been parsed yet
+ // Whether the CIB status section has been parsed yet
pcmk_sched_have_status = (1ULL << 17),
- //! Whether the cluster includes any Pacemaker Remote nodes (via CIB)
+ // Whether the cluster includes any Pacemaker Remote nodes (via CIB)
pcmk_sched_have_remote_nodes = (1ULL << 18),
- // The remaining flags are scheduling options that must be set explicitly
- /*!
+ /* The remaining flags are scheduling options that must be set explicitly */
+
+ /*
* Whether to skip unpacking the CIB status section and stop the scheduling
* sequence after applying node-specific location criteria (skipping
* assignment, ordering, actions, etc.).
*/
pcmk_sched_location_only = (1ULL << 20),
- //! Whether sensitive resource attributes have been masked
+ // Whether sensitive resource attributes have been masked
pcmk_sched_sanitized = (1ULL << 21),
- //! Skip counting of total, disabled, and blocked resource instances
+ // Skip counting of total, disabled, and blocked resource instances
pcmk_sched_no_counts = (1ULL << 23),
- /*!
+ /*
* Skip deprecated code kept solely for backward API compatibility
* (internal code should always set this)
*/
pcmk_sched_no_compat = (1ULL << 24),
- //! Whether node scores should be output instead of logged
+ // Whether node scores should be output instead of logged
pcmk_sched_output_scores = (1ULL << 25),
- //! Whether to show node and resource utilization (in log or output)
+ // Whether to show node and resource utilization (in log or output)
pcmk_sched_show_utilization = (1ULL << 26),
- /*!
+ /*
* Whether to stop the scheduling sequence after unpacking the CIB,
* calculating cluster status, and applying node health (skipping
* applying node-specific location criteria, assignment, etc.)
*/
pcmk_sched_validate_only = (1ULL << 27),
};
+//!@}
-//! Implementation of pcmk_scheduler_t
+// Implementation of pcmk_scheduler_t
+// @COMPAT Make contents internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
struct pe_working_set_s {
// Be careful about when each piece of information is available and final
- xmlNode *input; //!< CIB XML
- crm_time_t *now; //!< Current time for evaluation purposes
- char *dc_uuid; //!< Node ID of designated controller
- pcmk_node_t *dc_node; //!< Node object for DC
- const char *stonith_action; //!< Default fencing action
- const char *placement_strategy; //!< Value of placement-strategy property
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated To set scheduler iput, use pcmk_set_scheduler_cib() instead
+ xmlNode *input; // CIB XML
+
+ crm_time_t *now; // Current time for evaluation purposes
+ char *dc_uuid; // Node ID of designated controller
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_get_dc() instead
+ pcmk_node_t *dc_node; // Node object for DC
+ const char *stonith_action; // Default fencing action
+ const char *placement_strategy; // Value of placement-strategy property
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
// @COMPAT Change to uint64_t at a compatibility break
- unsigned long long flags; //!< Group of enum pcmk_scheduler_flags
+ //! \deprecated Call pcmk_has_quorum() to check quorum
+ unsigned long long flags; // Group of enum pcmk_scheduler_flags
+
+ int stonith_timeout; // Value of stonith-timeout property
+
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_get_no_quorum_policy() to get no-quorum policy
+ enum pe_quorum_policy no_quorum_policy; // Response to loss of quorum
- int stonith_timeout; //!< Value of stonith-timeout property
- enum pe_quorum_policy no_quorum_policy; //!< Response to loss of quorum
- GHashTable *config_hash; //!< Cluster properties
+ GHashTable *config_hash; // Cluster properties
- //!< Ticket constraints unpacked from ticket state
+ // Ticket constraints unpacked from ticket state
GHashTable *tickets;
- //! Actions for which there can be only one (such as "fence node X")
+ // Actions for which there can be only one (such as "fence node X")
GHashTable *singletons;
- GList *nodes; //!< Nodes in cluster
- GList *resources; //!< Resources in cluster
- GList *placement_constraints; //!< Location constraints
- GList *ordering_constraints; //!< Ordering constraints
- GList *colocation_constraints; //!< Colocation constraints
+ // NOTE: sbd (as of at least 1.5.2) uses this
+ //! \deprecated Call pcmk_find_node() to find a node instead
+ GList *nodes; // Nodes in cluster
- //!< Ticket constraints unpacked by libpacemaker
+ GList *resources; // Resources in cluster
+ GList *placement_constraints; // Location constraints
+ GList *ordering_constraints; // Ordering constraints
+ GList *colocation_constraints; // Colocation constraints
+
+ // Ticket constraints unpacked by libpacemaker
GList *ticket_constraints;
- GList *actions; //!< Scheduled actions
- xmlNode *failed; //!< History entries of failed actions
- xmlNode *op_defaults; //!< Configured operation defaults
- xmlNode *rsc_defaults; //!< Configured resource defaults
- int num_synapse; //!< Number of transition graph synapses
- int max_valid_nodes; //!< \deprecated Do not use
- int order_id; //!< ID to use for next created ordering
- int action_id; //!< ID to use for next created action
- xmlNode *graph; //!< Transition graph
- GHashTable *template_rsc_sets; //!< Mappings of template ID to resource ID
+ GList *actions; // Scheduled actions
+ xmlNode *failed; // History entries of failed actions
+ xmlNode *op_defaults; // Configured operation defaults
+ xmlNode *rsc_defaults; // Configured resource defaults
+ int num_synapse; // Number of transition graph synapses
+ int max_valid_nodes; // \deprecated Do not use
+ int order_id; // ID to use for next created ordering
+ int action_id; // ID to use for next created action
+ xmlNode *graph; // Transition graph
+ GHashTable *template_rsc_sets; // Mappings of template ID to resource ID
// @COMPAT Replace this with a fencer variable (only place it's used)
- const char *localhost; //!< \deprecated Do not use
-
- GHashTable *tags; //!< Configuration tags (ID -> pcmk_tag_t *)
- int blocked_resources; //!< Number of blocked resources in cluster
- int disabled_resources; //!< Number of disabled resources in cluster
- GList *param_check; //!< History entries that need to be checked
- GList *stop_needed; //!< Containers that need stop actions
- time_t recheck_by; //!< Hint to controller when to reschedule
- int ninstances; //!< Total number of resource instances
- guint shutdown_lock; //!< How long to lock resources (seconds)
- int priority_fencing_delay; //!< Priority fencing delay
+ const char *localhost; // \deprecated Do not use
+
+ GHashTable *tags; // Configuration tags (ID -> pcmk_tag_t *)
+ int blocked_resources; // Number of blocked resources in cluster
+ int disabled_resources; // Number of disabled resources in cluster
+ GList *param_check; // History entries that need to be checked
+ GList *stop_needed; // Containers that need stop actions
+ time_t recheck_by; // Hint to controller when to reschedule
+ int ninstances; // Total number of resource instances
+ guint shutdown_lock; // How long to lock resources (seconds)
+ int priority_fencing_delay; // Priority fencing delay
// pcmk__output_t *
- void *priv; //!< For Pacemaker use only
+ void *priv; // For Pacemaker use only
- guint node_pending_timeout; //!< Pending join times out after this (ms)
+ guint node_pending_timeout; // Pending join times out after this (ms)
};
+//!@}
+
+/* Whether the scheduler input currently being processed has warnings or errors
+ *
+ * @COMPAT When we can break API compatibility, we should make these
+ * internal-only. Ideally they would be converted to pcmk_scheduler_flags
+ * values, but everywhere they're needed doesn't currently have access to the
+ * scheduler data.
+ */
+//!@{
+//! \deprecated Do not use
+extern gboolean was_processing_error;
+extern gboolean was_processing_warning;
+//!@}
+
+pcmk_node_t *pcmk_get_dc(const pcmk_scheduler_t *scheduler);
+enum pe_quorum_policy pcmk_get_no_quorum_policy(const pcmk_scheduler_t
+ *scheduler);
+
+int pcmk_set_scheduler_cib(pcmk_scheduler_t *scheduler, xmlNode *cib);
+
+bool pcmk_has_quorum(const pcmk_scheduler_t *scheduler);
+pcmk_node_t *pcmk_find_node(const pcmk_scheduler_t *scheduler,
+ const char *node_name);
#ifdef __cplusplus
}
diff --git a/include/crm/common/scheduler_internal.h b/include/crm/common/scheduler_internal.h
index 1f1da9f..c990795 100644
--- a/include/crm/common/scheduler_internal.h
+++ b/include/crm/common/scheduler_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,43 +8,29 @@
*/
#ifndef PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H
-# define PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H
+#define PCMK__CRM_COMMON_SCHEDULER_INTERNAL__H
#include <crm/common/action_relation_internal.h>
+#include <crm/common/actions_internal.h>
+#include <crm/common/attrs_internal.h>
+#include <crm/common/bundles_internal.h>
#include <crm/common/clone_internal.h>
#include <crm/common/digests_internal.h>
#include <crm/common/failcounts_internal.h>
#include <crm/common/group_internal.h>
+#include <crm/common/history_internal.h>
+#include <crm/common/location_internal.h>
+#include <crm/common/nodes_internal.h>
+#include <crm/common/primitive_internal.h>
+#include <crm/common/remote_internal.h>
+#include <crm/common/resources_internal.h>
#include <crm/common/roles_internal.h>
+#include <crm/common/rules_internal.h>
#ifdef __cplusplus
extern "C" {
#endif
-/* Some warnings are too noisy when logged every time a give function is called
- * (for example, using a deprecated feature). As an alternative, we allow
- * warnings to be logged once per scheduler sequence (transition). Each of those
- * warnings needs a flag defined here.
- */
-enum pcmk__sched_warnings {
- pcmk__wo_blind = (1 << 0),
- pcmk__wo_restart_type = (1 << 1),
- pcmk__wo_role_after = (1 << 2),
- pcmk__wo_poweroff = (1 << 3),
- pcmk__wo_require_all = (1 << 4),
- pcmk__wo_order_score = (1 << 5),
- pcmk__wo_neg_threshold = (1 << 6),
- pcmk__wo_remove_after = (1 << 7),
- pcmk__wo_ping_node = (1 << 8),
- pcmk__wo_order_inst = (1 << 9),
- pcmk__wo_coloc_inst = (1 << 10),
- pcmk__wo_group_order = (1 << 11),
- pcmk__wo_group_coloc = (1 << 12),
- pcmk__wo_upstart = (1 << 13),
- pcmk__wo_nagios = (1 << 14),
- pcmk__wo_set_ordering = (1 << 15),
-};
-
enum pcmk__check_parameters {
/* Clear fail count if parameters changed for un-expired start or monitor
* last_failure.
@@ -57,9 +43,87 @@ enum pcmk__check_parameters {
pcmk__check_active,
};
-// Group of enum pcmk__sched_warnings flags for warnings we want to log once
+// Group of enum pcmk__warnings flags for warnings we want to log once
extern uint32_t pcmk__warnings;
+/*!
+ * \internal
+ * \brief Log a resource-tagged message at info severity
+ *
+ * \param[in] rsc Tag message with this resource's ID
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__rsc_info(rsc, fmt, args...) \
+ crm_log_tag(LOG_INFO, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)
+
+/*!
+ * \internal
+ * \brief Log a resource-tagged message at debug severity
+ *
+ * \param[in] rsc Tag message with this resource's ID
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__rsc_debug(rsc, fmt, args...) \
+ crm_log_tag(LOG_DEBUG, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)
+
+/*!
+ * \internal
+ * \brief Log a resource-tagged message at trace severity
+ *
+ * \param[in] rsc Tag message with this resource's ID
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__rsc_trace(rsc, fmt, args...) \
+ crm_log_tag(LOG_TRACE, ((rsc) == NULL)? "<NULL>" : (rsc)->id, (fmt), ##args)
+
+/*!
+ * \internal
+ * \brief Log an error and remember that current scheduler input has errors
+ *
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__sched_err(fmt...) do { \
+ was_processing_error = TRUE; \
+ crm_err(fmt); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Log a warning and remember that current scheduler input has warnings
+ *
+ * \param[in] fmt... printf(3)-style format and arguments
+ */
+#define pcmk__sched_warn(fmt...) do { \
+ was_processing_warning = TRUE; \
+ crm_warn(fmt); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Set scheduler flags
+ *
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] flags_to_set Group of enum pcmk_scheduler_flags to set
+ */
+#define pcmk__set_scheduler_flags(scheduler, flags_to_set) do { \
+ (scheduler)->flags = pcmk__set_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Scheduler", crm_system_name, \
+ (scheduler)->flags, (flags_to_set), #flags_to_set); \
+ } while (0)
+
+/*!
+ * \internal
+ * \brief Clear scheduler flags
+ *
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] flags_to_clear Group of enum pcmk_scheduler_flags to clear
+ */
+#define pcmk__clear_scheduler_flags(scheduler, flags_to_clear) do { \
+ (scheduler)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
+ LOG_TRACE, "Scheduler", crm_system_name, \
+ (scheduler)->flags, (flags_to_clear), #flags_to_clear); \
+ } while (0)
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/scheduler_types.h b/include/crm/common/scheduler_types.h
index 5c4a193..970d3e4 100644
--- a/include/crm/common/scheduler_types.h
+++ b/include/crm/common/scheduler_types.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 the Pacemaker project contributors
+ * Copyright 2023-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_SCHEDULER_TYPES__H
-# define PCMK__CRM_COMMON_SCHEDULER_TYPES__H
+#define PCMK__CRM_COMMON_SCHEDULER_TYPES__H
#ifdef __cplusplus
extern "C" {
diff --git a/include/crm/common/schemas.h b/include/crm/common/schemas.h
new file mode 100644
index 0000000..81fdc19
--- /dev/null
+++ b/include/crm/common/schemas.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_SCHEMAS__H
+#define PCMK__CRM_COMMON_SCHEMAS__H
+
+#include <stdbool.h> // bool
+#include <libxml/tree.h> // xmlNode
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \file
+ * \brief XML schema API
+ * \ingroup core
+ */
+
+int pcmk_update_configured_schema(xmlNode **xml, bool to_logs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_SCHEMAS__H
diff --git a/include/crm/common/schemas_internal.h b/include/crm/common/schemas_internal.h
new file mode 100644
index 0000000..0104016
--- /dev/null
+++ b/include/crm/common/schemas_internal.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2006-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.
+ */
+
+#ifndef PCMK__SCHEMAS_INTERNAL__H
+#define PCMK__SCHEMAS_INTERNAL__H
+
+#include <glib.h> // GList, gboolean
+#include <libxml/tree.h> // xmlNode, xmlRelaxNGValidityErrorFunc
+
+void crm_schema_init(void);
+void crm_schema_cleanup(void);
+
+void pcmk__load_schemas_from_dir(const char *dir);
+void pcmk__sort_schemas(void);
+GList *pcmk__schema_files_later_than(const char *name);
+void pcmk__build_schema_xml_node(xmlNode *parent, const char *name,
+ GList **already_included);
+const char *pcmk__remote_schema_dir(void);
+GList *pcmk__get_schema(const char *name);
+const char *pcmk__highest_schema_name(void);
+int pcmk__cmp_schemas_by_name(const char *schema1_name,
+ const char *schema2_name);
+bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation,
+ xmlRelaxNGValidityErrorFunc error_handler,
+ void *error_handler_context);
+bool pcmk__configured_schema_validates(xmlNode *xml);
+int pcmk__update_schema(xmlNode **xml, const char *max_schema_name,
+ bool transform, bool to_logs);
+void pcmk__warn_if_schema_deprecated(const char *schema);
+
+#endif // PCMK__SCHEMAS_INTERNAL__H
diff --git a/include/crm/common/scores.h b/include/crm/common/scores.h
new file mode 100644
index 0000000..4b73f66
--- /dev/null
+++ b/include/crm/common/scores.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_SCORES__H
+#define PCMK__CRM_COMMON_SCORES__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Pacemaker APIs related to scores
+ * \ingroup core
+ */
+
+//! Integer score to use to represent "infinity"
+#define PCMK_SCORE_INFINITY 1000000
+
+const char *pcmk_readable_score(int score);
+int char2score(const char *score);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_SCORES__H
diff --git a/include/crm/common/scores_compat.h b/include/crm/common/scores_compat.h
new file mode 100644
index 0000000..f4515d8
--- /dev/null
+++ b/include/crm/common/scores_compat.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_SCORES_COMPAT__H
+#define PCMK__CRM_COMMON_SCORES_COMPAT__H
+
+#include <sys/types.h> // size_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Deprecated Pacemaker score APIs
+ * \ingroup core
+ * \deprecated Do not include this header directly. The APIs in this header, and
+ * the header itself, will be removed in a future release.
+ */
+
+//! \deprecated Use pcmk_readable_score() instead
+char *score2char(int score);
+
+//! \deprecated Use pcmk_readable_score() instead
+char *score2char_stack(int score, char *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_SCORES_COMPAT__H
diff --git a/include/crm/common/scores_internal.h b/include/crm/common/scores_internal.h
new file mode 100644
index 0000000..ec9213c
--- /dev/null
+++ b/include/crm/common/scores_internal.h
@@ -0,0 +1,15 @@
+/*
+ * 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 Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#ifndef PCMK__CRM_COMMON_SCORES_INTERNAL__H
+#define PCMK__CRM_COMMON_SCORES_INTERNAL__H
+
+int pcmk__add_scores(int score1, int score2);
+
+#endif // PCMK__CRM_COMMON_SCORES_INTERNAL__H
diff --git a/include/crm/common/strings_internal.h b/include/crm/common/strings_internal.h
index cd394d9..a34a9f8 100644
--- a/include/crm/common/strings_internal.h
+++ b/include/crm/common/strings_internal.h
@@ -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.
*
@@ -52,6 +52,7 @@ GHashTable *pcmk__strkey_table(GDestroyNotify key_destroy_func,
GHashTable *pcmk__strikey_table(GDestroyNotify key_destroy_func,
GDestroyNotify value_destroy_func);
GHashTable *pcmk__str_table_dup(GHashTable *old_table);
+void pcmk__insert_dup(GHashTable *table, const char *name, const char *value);
/*!
* \internal
@@ -141,7 +142,24 @@ bool pcmk__char_in_any_str(int ch, ...) G_GNUC_NULL_TERMINATED;
int pcmk__strcmp(const char *s1, const char *s2, uint32_t flags);
int pcmk__numeric_strcasecmp(const char *s1, const char *s2);
+
+char *pcmk__str_copy_as(const char *file, const char *function, uint32_t line,
+ const char *str);
+
+/*!
+ * \internal
+ * \brief Copy a string, asserting on failure
+ *
+ * \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().
+ */
+#define pcmk__str_copy(str) pcmk__str_copy_as(__FILE__, __func__, __LINE__, str)
+
void pcmk__str_update(char **str, const char *value);
+
void pcmk__g_strcat(GString *buffer, ...) G_GNUC_NULL_TERMINATED;
static inline bool
@@ -208,7 +226,7 @@ pcmk__ttoa(time_t epoch_time)
static inline const char *
pcmk__btoa(bool condition)
{
- return condition? "true" : "false";
+ return condition? PCMK_VALUE_TRUE : PCMK_VALUE_FALSE;
}
#endif /* PCMK__STRINGS_INTERNAL__H */
diff --git a/include/crm/common/tags.h b/include/crm/common/tags.h
index 3f4861d..4e3736f 100644
--- a/include/crm/common/tags.h
+++ b/include/crm/common/tags.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_TAGS__H
-# define PCMK__CRM_COMMON_TAGS__H
+#define PCMK__CRM_COMMON_TAGS__H
#include <glib.h> // GList
@@ -22,11 +22,15 @@ extern "C" {
* \ingroup core
*/
-//! Configuration tag object
+// Configuration tag object
+// @COMPAT Make internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
typedef struct pe_tag_s {
- char *id; //!< XML ID of tag
- GList *refs; //!< XML IDs of objects that reference the tag
+ char *id; // XML ID of tag
+ GList *refs; // XML IDs of objects that reference the tag
} pcmk_tag_t;
+//!@}
#ifdef __cplusplus
}
diff --git a/include/crm/common/tickets.h b/include/crm/common/tickets.h
index 40079e9..cd33420 100644
--- a/include/crm/common/tickets.h
+++ b/include/crm/common/tickets.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#ifndef PCMK__CRM_COMMON_TICKETS__H
-# define PCMK__CRM_COMMON_TICKETS__H
+#define PCMK__CRM_COMMON_TICKETS__H
#include <sys/types.h> // time_t
#include <glib.h> // gboolean, GHashTable
@@ -23,14 +23,18 @@ extern "C" {
* \ingroup core
*/
-//! Ticket constraint object
+// Ticket constraint object
+// @COMPAT Make internal when we can break API backward compatibility
+//!@{
+//! \deprecated Do not use (public access will be removed in a future release)
typedef struct pe_ticket_s {
- char *id; //!< XML ID of ticket constraint or state
- gboolean granted; //!< Whether cluster has been granted the ticket
- time_t last_granted; //!< When cluster was last granted the ticket
- gboolean standby; //!< Whether ticket is temporarily suspended
- GHashTable *state; //!< XML attributes from ticket state
+ char *id; // XML ID of ticket constraint or state
+ gboolean granted; // Whether cluster has been granted the ticket
+ time_t last_granted; // When cluster was last granted the ticket
+ gboolean standby; // Whether ticket is temporarily suspended
+ GHashTable *state; // XML attributes from ticket state
} pcmk_ticket_t;
+//!@}
#ifdef __cplusplus
}
diff --git a/include/crm/common/unittest_internal.h b/include/crm/common/unittest_internal.h
index 1fc8501..565dcc9 100644
--- a/include/crm/common/unittest_internal.h
+++ b/include/crm/common/unittest_internal.h
@@ -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.
*
@@ -19,11 +19,83 @@
#include <cmocka.h>
+#include <crm/common/xml.h>
+
#ifndef CRM_COMMON_UNITTEST_INTERNAL__H
#define CRM_COMMON_UNITTEST_INTERNAL__H
/* internal unit testing related utilities */
+#if (PCMK__WITH_COVERAGE == 1)
+/* This function isn't exposed anywhere. The following prototype was taken from
+ * /usr/lib/gcc/x86_64-redhat-linux/??/include/gcov.h
+ */
+extern void __gcov_dump(void);
+#else
+#define __gcov_dump()
+#endif
+
+/*!
+ * \internal
+ * \brief Assert that the XML output from an API function is valid
+ *
+ * \param[in] xml The XML output of some public pacemaker API function
+ *
+ * Run the given XML through xmllint and attempt to validate it against the
+ * api-result.rng schema file. Assert if validation fails.
+ *
+ * \note PCMK_schema_directory needs to be set to the directory containing
+ * the built schema files before calling this function. Typically,
+ * this will be done in Makefile.am.
+ */
+void pcmk__assert_validates(xmlNode *xml);
+
+/*!
+ * \internal
+ * \brief Perform setup for a group of unit tests that will manipulate XML
+ *
+ * This function is suitable for being passed as the first argument to the
+ * \c PCMK__UNIT_TEST macro.
+ *
+ * \param[in] state The cmocka state object, currently unused by this
+ * function
+ */
+int pcmk__xml_test_setup_group(void **state);
+
+/*!
+ * \internal
+ * \brief Copy the given CIB file to a temporary file so it can be modified
+ * as part of doing unit tests, returning the full temporary file or
+ * \c NULL on error.
+ *
+ * This function should be called as part of the process of setting up any
+ * single unit test that would access and modify a CIB. That is, it should
+ * be called from whatever function is the second argument to
+ * cmocka_unit_test_setup_teardown.
+ *
+ * \param[in] in_file The filename of the input CIB file, which must
+ * exist in the \c $PCMK_CTS_CLI_DIR directory. This
+ * should only be the filename, not the complete
+ * path.
+ */
+char *pcmk__cib_test_copy_cib(const char *in_file);
+
+/*!
+ * \internal
+ * \brief Clean up whatever was done by a previous call to
+ * \c pcmk__cib_test_copy_cib.
+ *
+ * This function should be called as part of the process of tearing down
+ * any single unit test that accessed a CIB. That is, it should be called
+ * from whatever function is the third argument to
+ * \c cmocka_unit_test_setup_teardown.
+ *
+ * \param[in] out_path The complete path to the temporary CIB location.
+ * This is the return value of
+ * \c pcmk__cib_test_copy_cib.
+ */
+void pcmk__cib_test_cleanup(char *out_path);
+
/*!
* \internal
* \brief Assert that a statement aborts through CRM_ASSERT().
@@ -51,6 +123,7 @@
struct rlimit cores = { 0, 0 }; \
setrlimit(RLIMIT_CORE, &cores); \
stmt; \
+ __gcov_dump(); \
_exit(0); \
} else if (p > 0) { \
int wstatus = 0; \
@@ -67,6 +140,15 @@
/*!
* \internal
+ * \brief Assert that a statement aborts
+ *
+ * This is exactly the same as pcmk__assert_asserts (CRM_ASSERT() is implemented
+ * with abort()), but given a different name for clarity.
+ */
+#define pcmk__assert_aborts(stmt) pcmk__assert_asserts(stmt)
+
+/*!
+ * \internal
* \brief Assert that a statement exits with the expected exit status.
*
* \param[in] stmt Statement to execute; can be an expression.
@@ -87,6 +169,7 @@
struct rlimit cores = { 0, 0 }; \
setrlimit(RLIMIT_CORE, &cores); \
stmt; \
+ __gcov_dump(); \
_exit(CRM_EX_NONE); \
} else if (p > 0) { \
int wstatus = 0; \
diff --git a/include/crm/common/util.h b/include/crm/common/util.h
index c75a55e..2ae3f8e 100644
--- a/include/crm/common/util.h
+++ b/include/crm/common/util.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,20 +8,22 @@
*/
#ifndef PCMK__CRM_COMMON_UTIL__H
-# define PCMK__CRM_COMMON_UTIL__H
-
-# include <sys/types.h> // gid_t, mode_t, size_t, time_t, uid_t
-# include <stdlib.h>
-# include <stdbool.h>
-# include <stdint.h> // uint32_t
-# include <limits.h>
-# include <signal.h>
-# include <glib.h>
-
-# include <crm/common/acl.h>
-# include <crm/common/actions.h>
-# include <crm/common/agents.h>
-# include <crm/common/results.h>
+#define PCMK__CRM_COMMON_UTIL__H
+
+#include <sys/types.h> // gid_t, mode_t, size_t, time_t, uid_t
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h> // uint32_t
+#include <limits.h>
+#include <signal.h>
+#include <glib.h>
+
+#include <crm/common/acl.h>
+#include <crm/common/actions.h>
+#include <crm/common/agents.h>
+#include <crm/common/results.h>
+#include <crm/common/scores.h>
+#include <crm/common/nvpair.h>
#ifdef __cplusplus
extern "C" {
@@ -33,29 +35,28 @@ extern "C" {
* \ingroup core
*/
-
-# define ONLINESTATUS "online" // Status of an online client
-# define OFFLINESTATUS "offline" // Status of an offline client
-
/* public node attribute functions (from attrd_client.c) */
char *pcmk_promotion_score_name(const char *rsc_id);
/* public Pacemaker Remote functions (from remote.c) */
int crm_default_remote_port(void);
-/* public score-related functions (from scores.c) */
-const char *pcmk_readable_score(int score);
-int char2score(const char *score);
-int pcmk__add_scores(int score1, int score2);
-
/* public string functions (from strings.c) */
+
+// NOTE: sbd (as of at least 1.5.2) uses this
gboolean crm_is_true(const char *s);
+
int crm_str_to_boolean(const char *s, int *ret);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
long long crm_get_msec(const char *input);
+
char * crm_strip_trailing_newline(char *str);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
char *crm_strdup_printf(char const *format, ...) G_GNUC_PRINTF(1, 2);
-guint crm_parse_interval_spec(const char *input);
+int pcmk_parse_interval_spec(const char *input, guint *result_ms);
int compare_version(const char *version1, const char *version2);
@@ -98,9 +99,6 @@ pcmk_all_flags_set(uint64_t flag_group, uint64_t flags_to_check)
*/
#define pcmk_is_set(g, f) pcmk_all_flags_set((g), (f))
-char *crm_meta_name(const char *field);
-const char *crm_meta_value(GHashTable * hash, const char *field);
-
char *crm_md5sum(const char *buffer);
char *crm_generate_uuid(void);
@@ -115,8 +113,6 @@ int pcmk_daemon_user(uid_t *uid, gid_t *gid);
void crm_gnutls_global_init(void);
#endif
-char *pcmk_hostname(void);
-
bool pcmk_str_is_infinity(const char *s);
bool pcmk_str_is_minus_infinity(const char *s);
diff --git a/include/crm/common/util_compat.h b/include/crm/common/util_compat.h
index 7a60208..dad72ec 100644
--- a/include/crm/common/util_compat.h
+++ b/include/crm/common/util_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,11 +8,11 @@
*/
#ifndef PCMK__CRM_COMMON_UTIL_COMPAT__H
-# define PCMK__CRM_COMMON_UTIL_COMPAT__H
+#define PCMK__CRM_COMMON_UTIL_COMPAT__H
-# include <glib.h>
-# include <libxml/tree.h>
-# include <crm/common/util.h>
+#include <glib.h>
+#include <libxml/tree.h>
+#include <crm/common/util.h>
#ifdef __cplusplus
extern "C" {
@@ -27,7 +27,7 @@ extern "C" {
* release.
*/
-//! \deprecated Use crm_parse_interval_spec() instead
+//! \deprecated Do not use
#define crm_get_interval crm_parse_interval_spec
//! \deprecated Do not use
@@ -40,6 +40,7 @@ is_not_set(long long word, long long bit)
return ((word & bit) == 0);
}
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Use pcmk_is_set() or pcmk_all_flags_set() instead
static inline gboolean
is_set(long long word, long long bit)
@@ -86,7 +87,7 @@ long long crm_parse_ll(const char *text, const char *default_text);
int crm_parse_int(const char *text, const char *default_text);
//! \deprecated Use strtoll() instead
-# define crm_atoi(text, default_text) crm_parse_int(text, default_text)
+#define crm_atoi(text, default_text) crm_parse_int(text, default_text)
//! \deprecated Use g_str_hash() instead
guint g_str_hash_traditional(gconstpointer v);
@@ -158,11 +159,17 @@ crm_ttoa(time_t epoch_time)
//! \deprecated Do not use Pacemaker libraries for generic I/O
void crm_build_path(const char *path_c, mode_t mode);
-//! \deprecated Use pcmk_readable_score() instead
-char *score2char(int score);
+//! \deprecated Use \c pcmk_parse_interval_spec() instead
+guint crm_parse_interval_spec(const char *input);
+
+//! \deprecated Use \c PCMK_VALUE_ONLINE instead
+#define ONLINESTATUS PCMK_VALUE_ONLINE
+
+//! \deprecated Use \c PCMK_VALUE_OFFLINE instead
+#define OFFLINESTATUS PCMK_VALUE_OFFLINE
-//! \deprecated Use pcmk_readable_score() instead
-char *score2char_stack(int score, char *buf, size_t len);
+//! \deprecated Use \c uname() instead
+char *pcmk_hostname(void);
#ifdef __cplusplus
}
diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h
index ac839d3..fd7e185 100644
--- a/include/crm/common/xml.h
+++ b/include/crm/common/xml.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,22 +8,25 @@
*/
#ifndef PCMK__CRM_COMMON_XML__H
-# define PCMK__CRM_COMMON_XML__H
+#define PCMK__CRM_COMMON_XML__H
-# include <stdio.h>
-# include <sys/types.h>
-# include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
-# include <stdlib.h>
-# include <errno.h>
-# include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
-# include <libxml/tree.h>
-# include <libxml/xpath.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
-# include <crm/crm.h>
-# include <crm/common/nvpair.h>
+#include <crm/crm.h>
+#include <crm/common/nvpair.h>
+#include <crm/common/schemas.h>
+#include <crm/common/xml_io.h>
+#include <crm/common/xml_names.h>
#ifdef __cplusplus
extern "C" {
@@ -35,218 +38,35 @@ extern "C" {
* \ingroup core
*/
-/* Define compression parameters for IPC messages
- *
- * Compression costs a LOT, so we don't want to do it unless we're hitting
- * message limits. Currently, we use 128KB as the threshold, because higher
- * values don't play well with the heartbeat stack. With an earlier limit of
- * 10KB, compressing 184 of 1071 messages accounted for 23% of the total CPU
- * used by the cib.
- */
-# define CRM_BZ2_BLOCKS 4
-# define CRM_BZ2_WORK 20
-# define CRM_BZ2_THRESHOLD 128 * 1024
-
typedef const xmlChar *pcmkXmlStr;
-gboolean add_message_xml(xmlNode * msg, const char *field, xmlNode * xml);
-xmlNode *get_message_xml(const xmlNode *msg, const char *field);
-
-/*
- * \brief xmlCopyPropList ACLs-sensitive replacement expading i++ notation
- *
- * The gist is the same as with \c{xmlCopyPropList(target, src->properties)}.
- * The function exits prematurely when any attribute cannot be copied for
- * ACLs violation. Even without bailing out, the result can possibly be
- * incosistent with expectations in that case, hence the caller shall,
- * aposteriori, verify that no document-level-tracked denial was indicated
- * with \c{xml_acl_denied(target)} and drop whole such intermediate object.
- *
- * \param[in,out] target Element to receive attributes from #src element
- * \param[in] src Element carrying attributes to copy over to #target
- *
- * \note Original commit 1c632c506 sadly haven't stated which otherwise
- * assumed behaviours of xmlCopyPropList were missing beyond otherwise
- * custom extensions like said ACLs and "atomic increment" (that landed
- * later on, anyway).
- */
-void copy_in_properties(xmlNode *target, const xmlNode *src);
-
-void expand_plus_plus(xmlNode * target, const char *name, const char *value);
-void fix_plus_plus_recursive(xmlNode * target);
-
-/*
- * Create a node named "name" as a child of "parent"
- * If parent is NULL, creates an unconnected node.
- *
- * Returns the created node
- *
- */
-xmlNode *create_xml_node(xmlNode * parent, const char *name);
-
-/*
- * Create a node named "name" as a child of "parent", giving it the provided
- * text content.
- * If parent is NULL, creates an unconnected node.
- *
- * Returns the created node
- *
- */
-xmlNode *pcmk_create_xml_text_node(xmlNode * parent, const char *name, const char *content);
-
-/*
- * Create a new HTML node named "element_name" as a child of "parent", giving it the
- * provided text content. Optionally, apply a CSS #id and #class.
- *
- * Returns the created node.
- */
-xmlNode *pcmk_create_html_node(xmlNode * parent, const char *element_name, const char *id,
- const char *class_name, const char *text);
-
-/*
- *
- */
-void purge_diff_markers(xmlNode * a_node);
-
-/*
- * Returns a deep copy of src_node
- *
- */
-xmlNode *copy_xml(xmlNode * src_node);
-
-/*
- * Add a copy of xml_node to new_parent
- */
-xmlNode *add_node_copy(xmlNode * new_parent, xmlNode * xml_node);
-
-/*
- * XML I/O Functions
- *
- * Whitespace between tags is discarded.
- */
-xmlNode *filename2xml(const char *filename);
-
-xmlNode *stdin2xml(void);
-
-xmlNode *string2xml(const char *input);
-
-int write_xml_fd(const xmlNode *xml, const char *filename, int fd,
- gboolean compress);
-int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress);
-
-char *dump_xml_formatted(const xmlNode *xml);
-char *dump_xml_formatted_with_text(const xmlNode *xml);
-char *dump_xml_unformatted(const xmlNode *xml);
-
-/*
- * Diff related Functions
- */
-xmlNode *diff_xml_object(xmlNode * left, xmlNode * right, gboolean suppress);
-
-xmlNode *subtract_xml_object(xmlNode * parent, xmlNode * left, xmlNode * right,
- gboolean full, gboolean * changed, const char *marker);
-
-gboolean can_prune_leaf(xmlNode * xml_node);
/*
* Searching & Modifying
*/
-xmlNode *find_xml_node(const xmlNode *root, const char *search_path,
- gboolean must_find);
-
-void xml_remove_prop(xmlNode * obj, const char *name);
-
-gboolean replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update,
- gboolean delete_only);
-
-gboolean update_xml_child(xmlNode * child, xmlNode * to_update);
-
-int find_xml_children(xmlNode ** children, xmlNode * root,
- const char *tag, const char *field, const char *value,
- gboolean search_matches);
+// NOTE: sbd (as of at least 1.5.2) uses this
xmlNode *get_xpath_object(const char *xpath, xmlNode * xml_obj, int error_level);
-xmlNode *get_xpath_object_relative(const char *xpath, xmlNode * xml_obj, int error_level);
-
-static inline const char *
-crm_map_element_name(const xmlNode *xml)
-{
- if (xml == NULL) {
- return NULL;
- } else if (strcmp((const char *) xml->name, "master") == 0) {
- return "clone";
- } else {
- return (const char *) xml->name;
- }
-}
char *calculate_on_disk_digest(xmlNode * local_cib);
char *calculate_operation_digest(xmlNode * local_cib, const char *version);
char *calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter,
const char *version);
-/* schema-related functions (from schemas.c) */
-gboolean validate_xml(xmlNode * xml_blob, const char *validation, gboolean to_logs);
-gboolean validate_xml_verbose(const xmlNode *xml_blob);
-
-/*!
- * \brief Update CIB XML to most recent schema version
- *
- * "Update" means either actively employ XSLT-based transformation(s)
- * (if intermediate product to transform valid per its declared schema version,
- * transformation available, proceeded successfully with a result valid per
- * expectated newer schema version), or just try to bump the marked validating
- * schema until all gradually rising schema versions attested or the first
- * such attempt subsequently fails to validate. Which of the two styles will
- * be used depends on \p transform parameter (positive/negative, respectively).
- *
- * \param[in,out] xml_blob XML tree representing CIB, may be swapped with
- * an "updated" one
- * \param[out] best The highest configuration version (per its index
- * in the global schemas table) it was possible to
- * reach during the update steps while ensuring
- * the validity of the result; if no validation
- * success was observed against possibly multiple
- * schemas, the value is less or equal the result
- * of \c get_schema_version applied on the input
- * \p xml_blob value (unless that function maps it
- * to -1, then 0 would be used instead)
- * \param[in] max When \p transform is positive, this allows to
- * set upper boundary schema (per its index in the
- * global schemas table) beyond which it's forbidden
- * to update by the means of XSLT transformation
- * \param[in] transform Whether to employ XSLT-based transformation so
- * as to allow overcoming possible incompatibilities
- * between major schema versions (see above)
- * \param[in] to_logs If true, output notable progress info to
- * internal log streams; if false, to stderr
- *
- * \return \c pcmk_ok if no non-recoverable error encountered (up to
- * caller to evaluate if the update satisfies the requirements
- * per returned \p best value), negative value carrying the reason
- * otherwise
- */
-int update_validation(xmlNode **xml_blob, int *best, int max,
- gboolean transform, gboolean to_logs);
-
-int get_schema_version(const char *name);
-const char *get_schema_name(int version);
-const char *xml_latest_schema(void);
-gboolean cli_config_update(xmlNode ** xml, int *best_version, gboolean to_logs);
-
+// NOTE: sbd (as of at least 1.5.2) uses this
/*!
* \brief Initialize the CRM XML subsystem
*
* This method sets global XML settings and loads pacemaker schemas into the cache.
*/
void crm_xml_init(void);
+
void crm_xml_cleanup(void);
void pcmk_free_xml_subtree(xmlNode *xml);
-void free_xml(xmlNode * child);
-xmlNode *first_named_child(const xmlNode *parent, const char *name);
-xmlNode *crm_next_same_xml(const xmlNode *sibling);
+// NOTE: sbd (as of at least 1.5.2) uses this
+void free_xml(xmlNode * child);
xmlNode *sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive);
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path);
@@ -280,10 +100,6 @@ int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version);
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest);
-void save_xml_to_file(const xmlNode *xml, const char *desc,
- const char *filename);
-
-char * crm_xml_escape(const char *text);
void crm_xml_sanitize_id(char *id);
void crm_xml_set_id(xmlNode *xml, const char *format, ...) G_GNUC_PRINTF(2, 3);
diff --git a/include/crm/common/xml_compat.h b/include/crm/common/xml_compat.h
index 85e39ff..3582838 100644
--- a/include/crm/common/xml_compat.h
+++ b/include/crm/common/xml_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,11 +8,13 @@
*/
#ifndef PCMK__CRM_COMMON_XML_COMPAT__H
-# define PCMK__CRM_COMMON_XML_COMPAT__H
+#define PCMK__CRM_COMMON_XML_COMPAT__H
#include <glib.h> // gboolean
#include <libxml/tree.h> // xmlNode
-#include <crm/common/xml.h> // crm_xml_add()
+
+#include <crm/common/nvpair.h> // crm_xml_add()
+#include <crm/common/xml_names.h> // PCMK_XE_CLONE
#ifdef __cplusplus
extern "C" {
@@ -64,6 +66,7 @@ crm_xml_add_boolean(xmlNode *node, const char *name, gboolean value)
return crm_xml_add(node, name, (value? "true" : "false"));
}
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Use name member directly
static inline const char *
crm_element_name(const xmlNode *xml)
@@ -71,6 +74,124 @@ crm_element_name(const xmlNode *xml)
return (xml == NULL)? NULL : (const char *) xml->name;
}
+//! \deprecated Do not use
+char *crm_xml_escape(const char *text);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *copy_xml(xmlNode *src_node);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *add_node_copy(xmlNode *new_parent, xmlNode *xml_node);
+
+//! \deprecated Do not use
+void purge_diff_markers(xmlNode *a_node);
+
+//! \deprecated Do not use
+xmlNode *diff_xml_object(xmlNode *left, xmlNode *right, gboolean suppress);
+
+//! \deprecated Do not use
+xmlNode *subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
+ gboolean full, gboolean *changed,
+ const char *marker);
+
+//! \deprecated Do not use
+gboolean can_prune_leaf(xmlNode *xml_node);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *create_xml_node(xmlNode *parent, const char *name);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *pcmk_create_xml_text_node(xmlNode *parent, const char *name,
+ const char *content);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *pcmk_create_html_node(xmlNode *parent, const char *element_name,
+ const char *id, const char *class_name,
+ const char *text);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *first_named_child(const xmlNode *parent, const char *name);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *find_xml_node(const xmlNode *root, const char *search_path,
+ gboolean must_find);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *crm_next_same_xml(const xmlNode *sibling);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+void xml_remove_prop(xmlNode *obj, const char *name);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+gboolean replace_xml_child(xmlNode *parent, xmlNode *child, xmlNode *update,
+ gboolean delete_only);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+gboolean update_xml_child(xmlNode *child, xmlNode *to_update);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+int find_xml_children(xmlNode **children, xmlNode *root, const char *tag,
+ const char *field, const char *value,
+ gboolean search_matches);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *get_xpath_object_relative(const char *xpath, xmlNode *xml_obj,
+ int error_level);
+
+//! \deprecated Do not use
+void fix_plus_plus_recursive(xmlNode *target);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *get_message_xml(const xmlNode *msg, const char *field);
+
+//! \deprecated Do not use
+const char *xml_latest_schema(void);
+
+//! \deprecated Do not use
+const char *get_schema_name(int version);
+
+//! \deprecated Do not use
+int get_schema_version(const char *name);
+
+//! \deprecated Do not use
+int update_validation(xmlNode **xml_blob, int *best, int max,
+ gboolean transform, gboolean to_logs);
+
+//! \deprecated Do not use
+gboolean validate_xml(xmlNode *xml_blob, const char *validation,
+ gboolean to_logs);
+
+//! \deprecated Do not use
+gboolean validate_xml_verbose(const xmlNode *xml_blob);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use
+gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs);
+
+//! \deprecated Do not use
+static inline const char *
+crm_map_element_name(const xmlNode *xml)
+{
+ if (xml == NULL) {
+ return NULL;
+ } else if (strcmp((const char *) xml->name, "master") == 0) {
+ // Can't use PCMK__XE_PROMOTABLE_LEGACY because it's internal
+ return PCMK_XE_CLONE;
+ } else {
+ return (const char *) xml->name;
+ }
+}
+
+//! \deprecated Do not use
+void copy_in_properties(xmlNode *target, const xmlNode *src);
+
+//! \deprecated Do not use
+void expand_plus_plus(xmlNode * target, const char *name, const char *value);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h
index ddb4384..6d4490f 100644
--- a/include/crm/common/xml_internal.h
+++ b/include/crm/common/xml_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 the Pacemaker project contributors
+ * Copyright 2017-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,20 +8,23 @@
*/
#ifndef PCMK__XML_INTERNAL__H
-# define PCMK__XML_INTERNAL__H
+#define PCMK__XML_INTERNAL__H
/*
* Internal-only wrappers for and extensions to libxml2 (libxslt)
*/
-# include <stdlib.h>
-# include <stdio.h>
-# include <string.h>
+#include <stdlib.h>
+#include <stdint.h> // uint32_t
+#include <stdio.h>
+#include <string.h>
-# include <crm/crm.h> /* transitively imports qblog.h */
-# include <crm/common/output_internal.h>
+#include <crm/crm.h> /* transitively imports qblog.h */
+#include <crm/common/output_internal.h>
+#include <crm/common/xml_io_internal.h>
+#include <crm/common/xml_names_internal.h> // PCMK__XE_PROMOTABLE_LEGACY
-# include <libxml/relaxng.h>
+#include <libxml/relaxng.h>
/*!
* \brief Base for directing lib{xml2,xslt} log into standard libqb backend
@@ -145,6 +148,7 @@ enum pcmk__xml_fmt_options {
//! Include the closing tag of an XML element
pcmk__xml_fmt_close = (1 << 5),
+ // @COMPAT Can we start including text nodes unconditionally?
//! Include XML text nodes
pcmk__xml_fmt_text = (1 << 6),
@@ -168,25 +172,27 @@ int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml);
/* XML search strings for guest, remote and pacemaker_remote nodes */
/* search string to find CIB resources entries for cluster nodes */
-#define PCMK__XP_MEMBER_NODE_CONFIG \
- "//" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES \
- "/" XML_CIB_TAG_NODE "[not(@type) or @type='member']"
+#define PCMK__XP_MEMBER_NODE_CONFIG \
+ "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_NODES \
+ "/" PCMK_XE_NODE \
+ "[not(@" PCMK_XA_TYPE ") or @" PCMK_XA_TYPE "='" PCMK_VALUE_MEMBER "']"
/* search string to find CIB resources entries for guest nodes */
#define PCMK__XP_GUEST_NODE_CONFIG \
- "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \
- "//" XML_TAG_META_SETS "//" XML_CIB_TAG_NVPAIR \
- "[@name='" XML_RSC_ATTR_REMOTE_NODE "']"
+ "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE \
+ "//" PCMK_XE_META_ATTRIBUTES "//" PCMK_XE_NVPAIR \
+ "[@" PCMK_XA_NAME "='" PCMK_META_REMOTE_NODE "']"
/* search string to find CIB resources entries for remote nodes */
-#define PCMK__XP_REMOTE_NODE_CONFIG \
- "//" XML_TAG_CIB "//" XML_CIB_TAG_CONFIGURATION "//" XML_CIB_TAG_RESOURCE \
- "[@type='remote'][@provider='pacemaker']"
+#define PCMK__XP_REMOTE_NODE_CONFIG \
+ "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE \
+ "[@" PCMK_XA_TYPE "='" PCMK_VALUE_REMOTE "']" \
+ "[@" PCMK_XA_PROVIDER "='pacemaker']"
/* search string to find CIB node status entries for pacemaker_remote nodes */
-#define PCMK__XP_REMOTE_NODE_STATUS \
- "//" XML_TAG_CIB "//" XML_CIB_TAG_STATUS "//" XML_CIB_TAG_STATE \
- "[@" XML_NODE_IS_REMOTE "='true']"
+#define PCMK__XP_REMOTE_NODE_STATUS \
+ "//" PCMK_XE_CIB "//" PCMK_XE_STATUS "//" PCMK__XE_NODE_STATE \
+ "[@" PCMK_XA_REMOTE_NODE "='" PCMK_VALUE_TRUE "']"
/*!
* \internal
* \brief Serialize XML (using libxml) into provided descriptor
@@ -208,17 +214,105 @@ enum pcmk__xml_artefact_ns {
void pcmk__strip_xml_text(xmlNode *xml);
const char *pcmk__xe_add_last_written(xmlNode *xe);
-xmlNode *pcmk__xe_match(const xmlNode *parent, const char *node_name,
- const char *attr_n, const char *attr_v);
+xmlNode *pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
+ const char *attr_n, const char *attr_v);
+
+void pcmk__xe_remove_attr(xmlNode *element, const char *name);
+bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data);
void pcmk__xe_remove_matching_attrs(xmlNode *element,
bool (*match)(xmlAttrPtr, void *),
void *user_data);
+int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search);
+int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace);
+int pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags);
GString *pcmk__element_xpath(const xmlNode *xml);
/*!
* \internal
+ * \enum pcmk__xml_escape_type
+ * \brief Indicators of which XML characters to escape
+ *
+ * XML allows the escaping of special characters by replacing them with entity
+ * references (for example, <tt>"&quot;"</tt>) or character references (for
+ * example, <tt>"&#13;"</tt>).
+ *
+ * The special characters <tt>'&'</tt> (except as the beginning of an entity
+ * reference) and <tt>'<'</tt> are not allowed in their literal forms in XML
+ * character data. Character data is non-markup text (for example, the content
+ * of a text node). <tt>'>'</tt> is allowed under most circumstances; we escape
+ * it for safety and symmetry.
+ *
+ * For more details, see the "Character Data and Markup" section of the XML
+ * spec, currently section 2.4:
+ * https://www.w3.org/TR/xml/#dt-markup
+ *
+ * Attribute values are handled specially.
+ * * If an attribute value is delimited by single quotes, then single quotes
+ * must be escaped within the value.
+ * * Similarly, if an attribute value is delimited by double quotes, then double
+ * quotes must be escaped within the value.
+ * * A conformant XML processor replaces a literal whitespace character (tab,
+ * newline, carriage return, space) in an attribute value with a space
+ * (\c '#x20') character. However, a reference to a whitespace character (for
+ * example, \c "&#x0A;" for \c '\n') does not get replaced.
+ * * For more details, see the "Attribute-Value Normalization" section of the
+ * XML spec, currently section 3.3.3. Note that the default attribute type
+ * is CDATA; we don't deal with NMTOKENS, etc.:
+ * https://www.w3.org/TR/xml/#AVNormalize
+ *
+ * Pacemaker always delimits attribute values with double quotes, so there's no
+ * need to escape single quotes.
+ *
+ * Newlines and tabs should be escaped in attribute values when XML is
+ * serialized to text, so that future parsing preserves them rather than
+ * normalizing them to spaces.
+ *
+ * We always escape carriage returns, so that they're not converted to spaces
+ * during attribute-value normalization and because displaying them as literals
+ * is messy.
+ */
+enum pcmk__xml_escape_type {
+ /*!
+ * For text nodes.
+ * * Escape \c '<', \c '>', and \c '&' using entity references.
+ * * Do not escape \c '\n' and \c '\t'.
+ * * Escape other non-printing characters using character references.
+ */
+ pcmk__xml_escape_text,
+
+ /*!
+ * For attribute values.
+ * * Escape \c '<', \c '>', \c '&', and \c '"' using entity references.
+ * * Escape \c '\n', \c '\t', and other non-printing characters using
+ * character references.
+ */
+ pcmk__xml_escape_attr,
+
+ /* @COMPAT Drop escaping of at least '\n' and '\t' for
+ * pcmk__xml_escape_attr_pretty when openstack-info, openstack-floating-ip,
+ * and openstack-virtual-ip resource agents no longer depend on it.
+ *
+ * At time of writing, openstack-info may set a multiline value for the
+ * openstack_ports node attribute. The other two agents query the value and
+ * require it to be on one line with no spaces.
+ */
+ /*!
+ * For attribute values displayed in text output delimited by double quotes.
+ * * Escape \c '\n' as \c "\\n"
+ * * Escape \c '\r' as \c "\\r"
+ * * Escape \c '\t' as \c "\\t"
+ * * Escape \c '"' as \c "\\""
+ */
+ pcmk__xml_escape_attr_pretty,
+};
+
+bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type);
+char *pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type);
+
+/*!
+ * \internal
* \brief Get the root directory to scan XML artefacts of given kind for
*
* \param[in] ns governs the hierarchy nesting against the inherent root dir
@@ -242,6 +336,20 @@ char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns,
/*!
* \internal
+ * \brief Retrieve the value of the \c PCMK_XA_ID XML attribute
+ *
+ * \param[in] xml XML element to check
+ *
+ * \return Value of the \c PCMK_XA_ID attribute (may be \c NULL)
+ */
+static inline const char *
+pcmk__xe_id(const xmlNode *xml)
+{
+ return crm_element_value(xml, PCMK_XA_ID);
+}
+
+/*!
+ * \internal
* \brief Check whether an XML element is of a particular type
*
* \param[in] xml XML element to compare
@@ -296,25 +404,6 @@ pcmk__xml_next(const xmlNode *child)
/*!
* \internal
- * \brief Return first non-text child element of an XML node
- *
- * \param[in] parent XML node to check
- *
- * \return First child element of \p parent (or NULL if none)
- */
-static inline xmlNode *
-pcmk__xe_first_child(const xmlNode *parent)
-{
- xmlNode *child = (parent? parent->children : NULL);
-
- while (child && (child->type != XML_ELEMENT_NODE)) {
- child = child->next;
- }
- return child;
-}
-
-/*!
- * \internal
* \brief Return next non-text sibling element of an XML element
*
* \param[in] child XML element to check
@@ -332,6 +421,34 @@ pcmk__xe_next(const xmlNode *child)
return next;
}
+xmlNode *pcmk__xe_create(xmlNode *parent, const char *name);
+xmlNode *pcmk__xml_copy(xmlNode *parent, xmlNode *src);
+xmlNode *pcmk__xe_next_same(const xmlNode *node);
+
+void pcmk__xe_set_content(xmlNode *node, const char *format, ...)
+ G_GNUC_PRINTF(2, 3);
+
+/*!
+ * \internal
+ * \enum pcmk__xa_flags
+ * \brief Flags for operations affecting XML attributes
+ */
+enum pcmk__xa_flags {
+ //! Flag has no effect
+ pcmk__xaf_none = 0U,
+
+ //! Don't overwrite existing values
+ pcmk__xaf_no_overwrite = (1U << 0),
+
+ /*!
+ * Treat values as score updates where possible (see
+ * \c pcmk__xe_set_score())
+ */
+ pcmk__xaf_score_update = (1U << 1),
+};
+
+int pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags);
+
/*!
* \internal
* \brief Like pcmk__xe_set_props, but takes a va_list instead of
@@ -383,6 +500,20 @@ pcmk__xe_first_attr(const xmlNode *xe)
char *
pcmk__xpath_node_id(const char *xpath, const char *node);
+/*!
+ * \internal
+ * \brief Print an informational message if an xpath query returned multiple
+ * items with the same ID.
+ *
+ * \param[in,out] out The output object
+ * \param[in] search The xpath search result, most typically the result of
+ * calling cib->cmds->query().
+ * \param[in] name The name searched for
+ */
+void
+pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
+ const char *name);
+
/* internal XML-related utilities */
enum xml_private_flags {
@@ -434,6 +565,9 @@ pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
int (*handler)(xmlNode *xml, void *userdata),
void *userdata);
+bool pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
+ void *user_data);
+
static inline const char *
pcmk__xml_attr_value(const xmlAttr *attr)
{
@@ -441,8 +575,20 @@ pcmk__xml_attr_value(const xmlAttr *attr)
: (const char *) attr->children->content;
}
-gboolean pcmk__validate_xml(xmlNode *xml_blob, const char *validation,
- xmlRelaxNGValidityErrorFunc error_handler,
- void *error_handler_context);
+// @COMPAT Remove when v1 patchsets are removed
+xmlNode *pcmk__diff_v1_xml_object(xmlNode *left, xmlNode *right, bool suppress);
+
+// @COMPAT Drop when PCMK__XE_PROMOTABLE_LEGACY is removed
+static inline const char *
+pcmk__map_element_name(const xmlNode *xml)
+{
+ if (xml == NULL) {
+ return NULL;
+ } else if (pcmk__xe_is(xml, PCMK__XE_PROMOTABLE_LEGACY)) {
+ return PCMK_XE_CLONE;
+ } else {
+ return (const char *) xml->name;
+ }
+}
#endif // PCMK__XML_INTERNAL__H
diff --git a/include/crm/common/xml_io.h b/include/crm/common/xml_io.h
new file mode 100644
index 0000000..a5e454c
--- /dev/null
+++ b/include/crm/common/xml_io.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_XML_IO__H
+#define PCMK__CRM_COMMON_XML_IO__H
+
+#include <libxml/tree.h> // xmlNode
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Wrappers for and extensions to XML input/output functions
+ * \ingroup core
+ */
+
+/* Define compression parameters for IPC messages
+ *
+ * Compression costs a LOT, so we don't want to do it unless we're hitting
+ * message limits. Currently, we use 128KB as the threshold, because higher
+ * values don't play well with the heartbeat stack. With an earlier limit of
+ * 10KB, compressing 184 of 1071 messages accounted for 23% of the total CPU
+ * used by the cib.
+ */
+#define CRM_BZ2_BLOCKS 4
+#define CRM_BZ2_WORK 20
+#define CRM_BZ2_THRESHOLD (128 * 1024)
+
+void save_xml_to_file(const xmlNode *xml, const char *desc,
+ const char *filename);
+
+#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+#include <crm/common/xml_io_compat.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_XML_IO__H
diff --git a/include/crm/common/xml_io_compat.h b/include/crm/common/xml_io_compat.h
new file mode 100644
index 0000000..74e5f1d
--- /dev/null
+++ b/include/crm/common/xml_io_compat.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_XML_IO_COMPAT__H
+#define PCMK__CRM_COMMON_XML_IO_COMPAT__H
+
+#include <glib.h> // gboolean
+#include <libxml/tree.h> // xmlNode
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Deprecated Pacemaker XML I/O API
+ * \ingroup core
+ * \deprecated Do not include this header directly. The XML APIs in this
+ * header, and the header itself, will be removed in a future
+ * release.
+ */
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *filename2xml(const char *filename);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *stdin2xml(void);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+xmlNode *string2xml(const char *input);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+int write_xml_fd(const xmlNode *xml, const char *filename, int fd,
+ gboolean compress);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+char *dump_xml_formatted(const xmlNode *xml);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+char *dump_xml_formatted_with_text(const xmlNode *xml);
+
+//! \deprecated Do not use Pacemaker for general-purpose XML manipulation
+char *dump_xml_unformatted(const xmlNode *xml);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_XML_IO_COMPAT__H
diff --git a/include/crm/common/xml_io_internal.h b/include/crm/common/xml_io_internal.h
new file mode 100644
index 0000000..6c2b625
--- /dev/null
+++ b/include/crm/common/xml_io_internal.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017-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.
+ */
+
+#ifndef PCMK__XML_IO_INTERNAL__H
+#define PCMK__XML_IO_INTERNAL__H
+
+/*
+ * Internal-only wrappers for and extensions to libxml2 I/O
+ */
+
+#include <stdbool.h> // bool
+
+#include <glib.h> // GString
+#include <libxml/tree.h> // xmlNode
+
+xmlNode *pcmk__xml_read(const char *filename);
+xmlNode *pcmk__xml_parse(const char *input);
+
+void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
+ int depth);
+
+int pcmk__xml2fd(int fd, xmlNode *cur);
+int pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd,
+ bool compress, unsigned int *nbytes);
+int pcmk__xml_write_file(const xmlNode *xml, const char *filename,
+ bool compress, unsigned int *nbytes);
+
+#endif // PCMK__XML_IO_INTERNAL__H
diff --git a/include/crm/common/xml_names.h b/include/crm/common/xml_names.h
new file mode 100644
index 0000000..1de7fb9
--- /dev/null
+++ b/include/crm/common/xml_names.h
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_XML_NAMES__H
+#define PCMK__CRM_COMMON_XML_NAMES__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \file
+ * \brief Defined string constants for XML element and attribute names
+ * \ingroup core
+ */
+
+/* For consistency, new constants should start with "PCMK_", followed by:
+ * - "XE" for XML element names
+ * - "XA" for XML attribute names
+ * - "OPT" for cluster option (property) names
+ * - "META" for meta-attribute names
+ * - "VALUE" for enumerated values (such as for options or for XML attributes)
+ * - "NODE_ATTR" for node attribute names
+ *
+ * Old names that don't follow this policy should eventually be deprecated and
+ * replaced with names that do.
+ *
+ * Symbols should be public if the user may specify them somewhere (especially
+ * the CIB) or if they're part of a well-defined structure that a user may need
+ * to parse. They should be internal if they're used only internally to
+ * Pacemaker (such as daemon IPC/CPG message XML).
+ *
+ * Constants belong in the following locations:
+ * * "XE" and "XA": xml_names.h and xml_names_internal.h
+ * * "OPT", "META", and "VALUE": options.h and options_internal.h
+ * * "NODE_ATTR": nodes.h and nodes_internal.h
+ *
+ * For meta-attributes that can be specified as either XML attributes or nvpair
+ * names, use "META" unless using both "XA" and "META" constants adds clarity.
+ * An example is operation attributes, which can be specified either as
+ * attributes of the PCMK_XE_OP element or as nvpairs in a meta-attribute set
+ * beneath the PCMK_XE_OP element.
+ */
+
+/*
+ * XML elements
+ */
+
+#define PCMK_XE_ACL_GROUP "acl_group"
+#define PCMK_XE_ACL_PERMISSION "acl_permission"
+#define PCMK_XE_ACL_ROLE "acl_role"
+#define PCMK_XE_ACL_TARGET "acl_target"
+#define PCMK_XE_ACLS "acls"
+#define PCMK_XE_ACTION "action"
+#define PCMK_XE_ACTIONS "actions"
+#define PCMK_XE_AGENT "agent"
+#define PCMK_XE_AGENT_STATUS "agent-status"
+#define PCMK_XE_AGENTS "agents"
+#define PCMK_XE_ALERT "alert"
+#define PCMK_XE_ALERTS "alerts"
+#define PCMK_XE_ALLOCATIONS "allocations"
+#define PCMK_XE_ALLOCATIONS_UTILIZATIONS "allocations_utilizations"
+#define PCMK_XE_ATTRIBUTE "attribute"
+#define PCMK_XE_BAN "ban"
+#define PCMK_XE_BANS "bans"
+#define PCMK_XE_BUNDLE "bundle"
+#define PCMK_XE_CAPACITY "capacity"
+#define PCMK_XE_CHANGE "change"
+#define PCMK_XE_CHANGE_ATTR "change-attr"
+#define PCMK_XE_CHANGE_LIST "change-list"
+#define PCMK_XE_CHANGE_RESULT "change-result"
+#define PCMK_XE_CHECK "check"
+#define PCMK_XE_CIB "cib"
+#define PCMK_XE_CLONE "clone"
+#define PCMK_XE_CLUSTER_ACTION "cluster_action"
+#define PCMK_XE_CLUSTER_INFO "cluster-info"
+#define PCMK_XE_CLUSTER_OPTIONS "cluster_options"
+#define PCMK_XE_CLUSTER_PROPERTY_SET "cluster_property_set"
+#define PCMK_XE_CLUSTER_STATUS "cluster_status"
+#define PCMK_XE_COMMAND "command"
+#define PCMK_XE_CONFIGURATION "configuration"
+#define PCMK_XE_CONSTRAINT "constraint"
+#define PCMK_XE_CONSTRAINTS "constraints"
+#define PCMK_XE_CONTENT "content"
+#define PCMK_XE_CRM_CONFIG "crm_config"
+#define PCMK_XE_CRM_MON "crm_mon"
+#define PCMK_XE_CRM_MON_DISCONNECTED "crm-mon-disconnected"
+#define PCMK_XE_CURRENT_DC "current_dc"
+#define PCMK_XE_DATE_EXPRESSION "date_expression"
+#define PCMK_XE_DATE_SPEC "date_spec"
+#define PCMK_XE_DC "dc"
+#define PCMK_XE_DEPRECATED "deprecated"
+#define PCMK_XE_DIFF "diff"
+#define PCMK_XE_DIGEST "digest"
+#define PCMK_XE_DIGESTS "digests"
+#define PCMK_XE_DOCKER "docker"
+#define PCMK_XE_DURATION "duration"
+#define PCMK_XE_ERROR "error"
+#define PCMK_XE_ERRORS "errors"
+#define PCMK_XE_EXPRESSION "expression"
+#define PCMK_XE_FAILURE "failure"
+#define PCMK_XE_FAILURES "failures"
+#define PCMK_XE_FEATURE "feature"
+#define PCMK_XE_FEATURES "features"
+#define PCMK_XE_FENCE_EVENT "fence_event"
+#define PCMK_XE_FENCE_HISTORY "fence_history"
+#define PCMK_XE_FENCING_ACTION "fencing_action"
+#define PCMK_XE_FENCING_LEVEL "fencing-level"
+#define PCMK_XE_FENCING_TOPOLOGY "fencing-topology"
+#define PCMK_XE_GROUP "group"
+#define PCMK_XE_INJECT_ATTR "inject_attr"
+#define PCMK_XE_INJECT_SPEC "inject_spec"
+#define PCMK_XE_INSTANCE_ATTRIBUTES "instance_attributes"
+#define PCMK_XE_INSTRUCTION "instruction"
+#define PCMK_XE_ITEM "item"
+#define PCMK_XE_LAST_CHANGE "last_change"
+#define PCMK_XE_LAST_FENCED "last-fenced"
+#define PCMK_XE_LAST_UPDATE "last_update"
+#define PCMK_XE_LIST "list"
+#define PCMK_XE_LONGDESC "longdesc"
+#define PCMK_XE_META_ATTRIBUTES "meta_attributes"
+#define PCMK_XE_METADATA "metadata"
+#define PCMK_XE_MODIFICATIONS "modifications"
+#define PCMK_XE_MODIFY_NODE "modify_node"
+#define PCMK_XE_MODIFY_TICKET "modify_ticket"
+#define PCMK_XE_NETWORK "network"
+#define PCMK_XE_NODE "node"
+#define PCMK_XE_NODE_ACTION "node_action"
+#define PCMK_XE_NODE_ATTRIBUTES "node_attributes"
+#define PCMK_XE_NODE_HISTORY "node_history"
+#define PCMK_XE_NODE_INFO "node-info"
+#define PCMK_XE_NODE_WEIGHT "node_weight"
+#define PCMK_XE_NODES "nodes"
+#define PCMK_XE_NODES_CONFIGURED "nodes_configured"
+#define PCMK_XE_NVPAIR "nvpair"
+#define PCMK_XE_OBJ_REF "obj_ref"
+#define PCMK_XE_OP "op"
+#define PCMK_XE_OP_DEFAULTS "op_defaults"
+#define PCMK_XE_OP_EXPRESSION "op_expression"
+#define PCMK_XE_OPERATION "operation"
+#define PCMK_XE_OPERATION_HISTORY "operation_history"
+#define PCMK_XE_OPERATIONS "operations"
+#define PCMK_XE_OPTION "option"
+#define PCMK_XE_OUTPUT "output"
+#define PCMK_XE_OVERRIDE "override"
+#define PCMK_XE_OVERRIDES "overrides"
+#define PCMK_XE_PACEMAKER_RESULT "pacemaker-result"
+#define PCMK_XE_PACEMAKERD "pacemakerd"
+#define PCMK_XE_PARAMETER "parameter"
+#define PCMK_XE_PARAMETERS "parameters"
+#define PCMK_XE_PODMAN "podman"
+#define PCMK_XE_PORT_MAPPING "port-mapping"
+#define PCMK_XE_POSITION "position"
+#define PCMK_XE_PRIMITIVE "primitive"
+#define PCMK_XE_PROMOTION_SCORE "promotion_score"
+#define PCMK_XE_PROVIDER "provider"
+#define PCMK_XE_PROVIDERS "providers"
+#define PCMK_XE_PSEUDO_ACTION "pseudo_action"
+#define PCMK_XE_REASON "reason"
+#define PCMK_XE_RECIPIENT "recipient"
+#define PCMK_XE_REPLICA "replica"
+#define PCMK_XE_RESOURCE "resource"
+#define PCMK_XE_RESOURCE_AGENT "resource-agent"
+#define PCMK_XE_RESOURCE_AGENT_ACTION "resource-agent-action"
+#define PCMK_XE_RESOURCE_CONFIG "resource_config"
+#define PCMK_XE_RESOURCE_HISTORY "resource_history"
+#define PCMK_XE_RESOURCE_REF "resource_ref"
+#define PCMK_XE_RESOURCE_SET "resource_set"
+#define PCMK_XE_RESOURCES "resources"
+#define PCMK_XE_RESOURCES_CONFIGURED "resources_configured"
+#define PCMK_XE_RESULT_CODE "result-code"
+#define PCMK_XE_REVISED_CLUSTER_STATUS "revised_cluster_status"
+#define PCMK_XE_ROLE "role"
+#define PCMK_XE_RSC_ACTION "rsc_action"
+#define PCMK_XE_RSC_COLOCATION "rsc_colocation"
+#define PCMK_XE_RSC_DEFAULTS "rsc_defaults"
+#define PCMK_XE_RSC_EXPRESSION "rsc_expression"
+#define PCMK_XE_RSC_LOCATION "rsc_location"
+#define PCMK_XE_RSC_ORDER "rsc_order"
+#define PCMK_XE_RSC_TICKET "rsc_ticket"
+#define PCMK_XE_RULE "rule"
+#define PCMK_XE_RULE_CHECK "rule-check"
+#define PCMK_XE_SELECT "select"
+#define PCMK_XE_SELECT_ATTRIBUTES "select_attributes"
+#define PCMK_XE_SELECT_FENCING "select_fencing"
+#define PCMK_XE_SELECT_NODES "select_nodes"
+#define PCMK_XE_SELECT_RESOURCES "select_resources"
+#define PCMK_XE_SHADOW "shadow"
+#define PCMK_XE_SHORTDESC "shortdesc"
+#define PCMK_XE_SOURCE "source"
+#define PCMK_XE_SPECIAL "special"
+#define PCMK_XE_STACK "stack"
+#define PCMK_XE_STATUS "status"
+#define PCMK_XE_STORAGE "storage"
+#define PCMK_XE_STORAGE_MAPPING "storage-mapping"
+#define PCMK_XE_SUMMARY "summary"
+#define PCMK_XE_TAG "tag"
+#define PCMK_XE_TAGS "tags"
+#define PCMK_XE_TARGET "target"
+#define PCMK_XE_TEMPLATE "template"
+#define PCMK_XE_TICKET "ticket"
+#define PCMK_XE_TICKETS "tickets"
+#define PCMK_XE_TIMING "timing"
+#define PCMK_XE_TIMINGS "timings"
+#define PCMK_XE_TRANSITION "transition"
+#define PCMK_XE_UTILIZATION "utilization"
+#define PCMK_XE_UTILIZATIONS "utilizations"
+#define PCMK_XE_VALIDATE "validate"
+#define PCMK_XE_VERSION "version"
+#define PCMK_XE_XML "xml"
+#define PCMK_XE_XML_PATCHSET "xml-patchset"
+
+
+/*
+ * XML attributes
+ */
+
+#define PCMK_XA_ACTION "action"
+#define PCMK_XA_ACTIVE "active"
+#define PCMK_XA_ADD_HOST "add-host"
+#define PCMK_XA_ADMIN_EPOCH "admin_epoch"
+#define PCMK_XA_ADVANCED "advanced"
+#define PCMK_XA_AGENT "agent"
+#define PCMK_XA_API_VERSION "api-version"
+#define PCMK_XA_ATTRIBUTE "attribute"
+#define PCMK_XA_AUTHOR "author"
+#define PCMK_XA_AUTOMATIC "automatic"
+#define PCMK_XA_BLOCKED "blocked"
+#define PCMK_XA_BOOLEAN_OP "boolean-op"
+#define PCMK_XA_BUILD "build"
+#define PCMK_XA_CACHED "cached"
+#define PCMK_XA_CALL "call"
+#define PCMK_XA_CIB_LAST_WRITTEN "cib-last-written"
+#define PCMK_XA_CIB_NODE "cib_node"
+#define PCMK_XA_CLASS "class"
+#define PCMK_XA_CLIENT "client"
+#define PCMK_XA_CODE "code"
+#define PCMK_XA_COMMENT "comment"
+#define PCMK_XA_COMPLETED "completed"
+#define PCMK_XA_CONTROL_PORT "control-port"
+#define PCMK_XA_COUNT "count"
+#define PCMK_XA_CRM_DEBUG_ORIGIN "crm-debug-origin"
+#define PCMK_XA_CRM_FEATURE_SET "crm_feature_set"
+#define PCMK_XA_CRM_TIMESTAMP "crm-timestamp"
+#define PCMK_XA_CRMD "crmd"
+#define PCMK_XA_DAYS "days"
+#define PCMK_XA_DC_UUID "dc-uuid"
+#define PCMK_XA_DEFAULT "default"
+#define PCMK_XA_DELEGATE "delegate"
+#define PCMK_XA_DESCRIPTION "description"
+#define PCMK_XA_DEST "dest"
+#define PCMK_XA_DEVICE "device"
+#define PCMK_XA_DEVICES "devices"
+#define PCMK_XA_DISABLED "disabled"
+#define PCMK_XA_DURATION "duration"
+#define PCMK_XA_END "end"
+#define PCMK_XA_EPOCH "epoch"
+#define PCMK_XA_EXEC "exec"
+#define PCMK_XA_EXEC_TIME "exec-time"
+#define PCMK_XA_EXECUTION_CODE "execution_code"
+#define PCMK_XA_EXECUTION_DATE "execution-date"
+#define PCMK_XA_EXECUTION_MESSAGE "execution_message"
+#define PCMK_XA_EXIT_REASON "exit-reason"
+#define PCMK_XA_EXITCODE "exitcode"
+#define PCMK_XA_EXITREASON "exitreason"
+#define PCMK_XA_EXITSTATUS "exitstatus"
+#define PCMK_XA_EXPECTED "expected"
+#define PCMK_XA_EXPECTED_UP "expected_up"
+#define PCMK_XA_EXPIRES "expires"
+#define PCMK_XA_EXTENDED_STATUS "extended-status"
+#define PCMK_XA_FAIL_COUNT "fail-count"
+#define PCMK_XA_FAILED "failed"
+#define PCMK_XA_FAILURE_IGNORED "failure_ignored"
+#define PCMK_XA_FEATURE_SET "feature_set"
+#define PCMK_XA_FEATURES "features"
+#define PCMK_XA_FILE "file"
+#define PCMK_XA_FIRST "first"
+#define PCMK_XA_FIRST_ACTION "first-action"
+#define PCMK_XA_FOR "for"
+#define PCMK_XA_FORMAT "format"
+#define PCMK_XA_FUNCTION "function"
+#define PCMK_XA_GENERATED "generated"
+#define PCMK_XA_HASH "hash"
+#define PCMK_XA_HAVE_QUORUM "have-quorum"
+#define PCMK_XA_HEALTH "health"
+#define PCMK_XA_HOST "host"
+#define PCMK_XA_HOST_INTERFACE "host-interface"
+#define PCMK_XA_HOST_NETMASK "host-netmask"
+#define PCMK_XA_HOURS "hours"
+#define PCMK_XA_ID "id"
+#define PCMK_XA_ID_AS_RESOURCE "id_as_resource"
+#define PCMK_XA_ID_REF "id-ref"
+#define PCMK_XA_IMAGE "image"
+#define PCMK_XA_INDEX "index"
+#define PCMK_XA_INFLUENCE "influence"
+#define PCMK_XA_INSTANCE "instance"
+#define PCMK_XA_INTERNAL_PORT "internal-port"
+#define PCMK_XA_INTERVAL "interval"
+#define PCMK_XA_IP_RANGE_START "ip-range-start"
+#define PCMK_XA_IS_DC "is_dc"
+#define PCMK_XA_KIND "kind"
+#define PCMK_XA_LANG "lang"
+#define PCMK_XA_LAST_FAILURE "last-failure"
+#define PCMK_XA_LAST_GRANTED "last-granted"
+#define PCMK_XA_LAST_RC_CHANGE "last-rc-change"
+#define PCMK_XA_LAST_UPDATED "last_updated"
+#define PCMK_XA_LOCKED_TO "locked_to"
+#define PCMK_XA_LOCKED_TO_HYPHEN "locked-to"
+#define PCMK_XA_LOSS_POLICY "loss-policy"
+#define PCMK_XA_MAINTENANCE "maintenance"
+#define PCMK_XA_MAINTENANCE_MODE "maintenance-mode"
+#define PCMK_XA_MANAGED "managed"
+#define PCMK_XA_MESSAGE "message"
+#define PCMK_XA_MINUTES "minutes"
+#define PCMK_XA_MIXED_VERSION "mixed_version"
+#define PCMK_XA_MONTHDAYS "monthdays"
+#define PCMK_XA_MONTHS "months"
+#define PCMK_XA_MULTI_STATE "multi_state"
+#define PCMK_XA_NAME "name"
+#define PCMK_XA_NETWORK "network"
+#define PCMK_XA_NEXT_ROLE "next-role"
+#define PCMK_XA_NO_QUORUM_PANIC "no-quorum-panic"
+#define PCMK_XA_NO_QUORUM_POLICY "no-quorum-policy"
+#define PCMK_XA_NODE "node"
+#define PCMK_XA_NODE_ATTRIBUTE "node-attribute"
+#define PCMK_XA_NODE_NAME "node_name"
+#define PCMK_XA_NODE_PATH "node_path"
+#define PCMK_XA_NODEID "nodeid"
+#define PCMK_XA_NODES_RUNNING_ON "nodes_running_on"
+#define PCMK_XA_NUM_UPDATES "num_updates"
+#define PCMK_XA_NUMBER "number"
+#define PCMK_XA_NUMBER_RESOURCES "number_resources"
+#define PCMK_XA_OBJECT_TYPE "object-type"
+#define PCMK_XA_ON_TARGET "on_target"
+#define PCMK_XA_ONLINE "online"
+#define PCMK_XA_OP "op"
+#define PCMK_XA_OP_KEY "op_key"
+#define PCMK_XA_OPERATION "operation"
+#define PCMK_XA_OPTIONS "options"
+#define PCMK_XA_ORIGIN "origin"
+#define PCMK_XA_ORPHAN "orphan"
+#define PCMK_XA_ORPHANED "orphaned"
+#define PCMK_XA_PACEMAKERD_STATE "pacemakerd-state"
+#define PCMK_XA_PATH "path"
+#define PCMK_XA_PENDING "pending"
+#define PCMK_XA_PORT "port"
+#define PCMK_XA_PRESENT "present"
+#define PCMK_XA_PRIORITY_FENCING_DELAY_MS "priority-fencing-delay-ms"
+#define PCMK_XA_PROGRAM "program"
+#define PCMK_XA_PROMOTABLE "promotable"
+#define PCMK_XA_PROMOTED_MAX "promoted-max"
+#define PCMK_XA_PROMOTED_ONLY "promoted-only"
+#define PCMK_XA_PROVIDER "provider"
+#define PCMK_XA_QUEUE_TIME "queue-time"
+#define PCMK_XA_QUEUED "queued"
+#define PCMK_XA_QUORUM "quorum"
+#define PCMK_XA_RANGE "range"
+#define PCMK_XA_RC "rc"
+#define PCMK_XA_RC_TEXT "rc_text"
+#define PCMK_XA_REASON "reason"
+#define PCMK_XA_REFERENCE "reference"
+#define PCMK_XA_RELOADABLE "reloadable"
+#define PCMK_XA_REMAIN_STOPPED "remain_stopped"
+#define PCMK_XA_REMOTE_CLEAR_PORT "remote-clear-port"
+#define PCMK_XA_REMOTE_NODE "remote_node"
+#define PCMK_XA_REMOTE_TLS_PORT "remote-tls-port"
+#define PCMK_XA_REPLICAS "replicas"
+#define PCMK_XA_REPLICAS_PER_HOST "replicas-per-host"
+#define PCMK_XA_REQUEST "request"
+#define PCMK_XA_REQUIRE_ALL "require-all"
+#define PCMK_XA_RESOURCE "resource"
+#define PCMK_XA_RESOURCE_AGENT "resource_agent"
+#define PCMK_XA_RESOURCE_DISCOVERY "resource-discovery"
+#define PCMK_XA_RESOURCES_RUNNING "resources_running"
+#define PCMK_XA_RESULT "result"
+#define PCMK_XA_ROLE "role"
+#define PCMK_XA_RSC "rsc"
+#define PCMK_XA_RSC_PATTERN "rsc-pattern"
+#define PCMK_XA_RSC_ROLE "rsc-role"
+#define PCMK_XA_RULE_ID "rule-id"
+#define PCMK_XA_RUN_COMMAND "run-command"
+#define PCMK_XA_RUNNING "running"
+#define PCMK_XA_RUNNING_ON "running_on"
+#define PCMK_XA_SCOPE "scope"
+#define PCMK_XA_SCORE "score"
+#define PCMK_XA_SCORE_ATTRIBUTE "score-attribute"
+#define PCMK_XA_SEQUENTIAL "sequential"
+#define PCMK_XA_SECONDS "seconds"
+#define PCMK_XA_SHUTDOWN "shutdown"
+#define PCMK_XA_SOURCE "source"
+#define PCMK_XA_SOURCE_DIR "source-dir"
+#define PCMK_XA_SOURCE_DIR_ROOT "source-dir-root"
+#define PCMK_XA_SPEC "spec"
+#define PCMK_XA_STANDARD "standard"
+#define PCMK_XA_STANDBY "standby"
+#define PCMK_XA_STANDBY_ONFAIL "standby_onfail"
+#define PCMK_XA_START "start"
+#define PCMK_XA_STATE "state"
+#define PCMK_XA_STATUS "status"
+#define PCMK_XA_STONITH_ENABLED "stonith-enabled"
+#define PCMK_XA_STONITH_TIMEOUT_MS "stonith-timeout-ms"
+#define PCMK_XA_STOP_ALL_RESOURCES "stop-all-resources"
+#define PCMK_XA_SYMMETRIC_CLUSTER "symmetric-cluster"
+#define PCMK_XA_SYMMETRICAL "symmetrical"
+#define PCMK_XA_SYS_FROM "sys_from"
+#define PCMK_XA_TAG "tag"
+#define PCMK_XA_TARGET "target"
+#define PCMK_XA_TARGET_ATTRIBUTE "target-attribute"
+#define PCMK_XA_TARGET_DIR "target-dir"
+#define PCMK_XA_TARGET_PATTERN "target-pattern"
+#define PCMK_XA_TARGET_ROLE "target_role"
+#define PCMK_XA_TARGET_VALUE "target-value"
+#define PCMK_XA_TASK "task"
+#define PCMK_XA_TEMPLATE "template"
+#define PCMK_XA_TICKET "ticket"
+#define PCMK_XA_TIME "time"
+#define PCMK_XA_THEN "then"
+#define PCMK_XA_THEN_ACTION "then-action"
+#define PCMK_XA_TYPE "type"
+#define PCMK_XA_UNAME "uname"
+#define PCMK_XA_UNCLEAN "unclean"
+#define PCMK_XA_UNHEALTHY "unhealthy"
+#define PCMK_XA_UNIQUE "unique"
+#define PCMK_XA_UNMANAGED "unmanaged"
+#define PCMK_XA_UPDATE_CLIENT "update-client"
+#define PCMK_XA_UPDATE_ORIGIN "update-origin"
+#define PCMK_XA_UPDATE_USER "update-user"
+#define PCMK_XA_USER "user"
+#define PCMK_XA_VALID "valid"
+#define PCMK_XA_VALIDATE_WITH "validate-with"
+#define PCMK_XA_VALUE "value"
+#define PCMK_XA_VALUE_SOURCE "value-source"
+#define PCMK_XA_VERSION "version"
+#define PCMK_XA_WATCHDOG "watchdog"
+#define PCMK_XA_WEEKDAYS "weekdays"
+#define PCMK_XA_WEEKS "weeks"
+#define PCMK_XA_WEEKYEARS "weekyears"
+#define PCMK_XA_WEIGHT "weight"
+#define PCMK_XA_WHEN "when"
+#define PCMK_XA_WITH_QUORUM "with_quorum"
+#define PCMK_XA_WITH_RSC "with-rsc"
+#define PCMK_XA_WITH_RSC_ROLE "with-rsc-role"
+#define PCMK_XA_XPATH "xpath"
+#define PCMK_XA_YEARDAYS "yeardays"
+#define PCMK_XA_YEARS "years"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_XML_NAMES__H
diff --git a/include/crm/common/xml_names_internal.h b/include/crm/common/xml_names_internal.h
new file mode 100644
index 0000000..a23f855
--- /dev/null
+++ b/include/crm/common/xml_names_internal.h
@@ -0,0 +1,353 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
+#define PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * XML element names used only by internal code
+ */
+
+#define PCMK__XE_ACK "ack"
+#define PCMK__XE_ATTRIBUTES "attributes"
+#define PCMK__XE_CIB_CALLBACK "cib-callback"
+#define PCMK__XE_CIB_CALLDATA "cib_calldata"
+#define PCMK__XE_CIB_COMMAND "cib_command"
+#define PCMK__XE_CIB_REPLY "cib-reply"
+#define PCMK__XE_CIB_RESULT "cib_result"
+#define PCMK__XE_CIB_TRANSACTION "cib_transaction"
+#define PCMK__XE_CIB_UPDATE_RESULT "cib_update_result"
+#define PCMK__XE_COPY "copy"
+#define PCMK__XE_CRM_EVENT "crm_event"
+#define PCMK__XE_CRM_XML "crm_xml"
+#define PCMK__XE_DIV "div"
+#define PCMK__XE_DOWNED "downed"
+#define PCMK__XE_EXIT_NOTIFICATION "exit-notification"
+#define PCMK__XE_FAILED_UPDATE "failed_update"
+#define PCMK__XE_GENERATION_TUPLE "generation_tuple"
+#define PCMK__XE_LRM "lrm"
+#define PCMK__XE_LRM_RESOURCE "lrm_resource"
+#define PCMK__XE_LRM_RESOURCES "lrm_resources"
+#define PCMK__XE_LRM_RSC_OP "lrm_rsc_op"
+#define PCMK__XE_LRMD_ALERT "lrmd_alert"
+#define PCMK__XE_LRMD_CALLDATA "lrmd_calldata"
+#define PCMK__XE_LRMD_COMMAND "lrmd_command"
+#define PCMK__XE_LRMD_IPC_MSG "lrmd_ipc_msg"
+#define PCMK__XE_LRMD_IPC_PROXY "lrmd_ipc_proxy"
+#define PCMK__XE_LRMD_NOTIFY "lrmd_notify"
+#define PCMK__XE_LRMD_REPLY "lrmd_reply"
+#define PCMK__XE_LRMD_RSC "lrmd_rsc"
+#define PCMK__XE_LRMD_RSC_OP "lrmd_rsc_op"
+#define PCMK__XE_MAINTENANCE "maintenance"
+#define PCMK__XE_META "meta"
+#define PCMK__XE_NACK "nack"
+#define PCMK__XE_NODE_STATE "node_state"
+#define PCMK__XE_NOTIFY "notify"
+#define PCMK__XE_OPTIONS "options"
+#define PCMK__XE_PARAM "param"
+#define PCMK__XE_PING "ping"
+#define PCMK__XE_PING_RESPONSE "ping_response"
+#define PCMK__XE_PSEUDO_EVENT "pseudo_event"
+#define PCMK__XE_RESOURCE_SETTINGS "resource-settings"
+#define PCMK__XE_RSC_OP "rsc_op"
+#define PCMK__XE_SHUTDOWN "shutdown"
+#define PCMK__XE_SPAN "span"
+#define PCMK__XE_ST_ASYNC_TIMEOUT_VALUE "st-async-timeout-value"
+#define PCMK__XE_ST_CALLDATA "st_calldata"
+#define PCMK__XE_ST_DEVICE_ACTION "st_device_action"
+#define PCMK__XE_ST_DEVICE_ID "st_device_id"
+#define PCMK__XE_ST_HISTORY "st_history"
+#define PCMK__XE_ST_NOTIFY_FENCE "st_notify_fence"
+#define PCMK__XE_ST_REPLY "st-reply"
+#define PCMK__XE_STONITH_COMMAND "stonith_command"
+#define PCMK__XE_TICKET_STATE "ticket_state"
+#define PCMK__XE_TRANSIENT_ATTRIBUTES "transient_attributes"
+#define PCMK__XE_TRANSITION_GRAPH "transition_graph"
+#define PCMK__XE_XPATH_QUERY "xpath-query"
+#define PCMK__XE_XPATH_QUERY_PATH "xpath-query-path"
+
+// @COMPAT Deprecated since 1.1.12
+#define PCMK__XE_ACL_USER "acl_user"
+
+/* @COMPAT Deprecate somehow. It's undocumented and behaves the same as
+ * PCMK__XE_CIB in places where it's recognized.
+ */
+#define PCMK__XE_ALL "all"
+
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XE_CIB_GENERATION "cib_generation"
+
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XE_CIB_UPDATE "cib_update"
+
+// @COMPAT Deprecated since 1.1.12; used with legacy CIB updates
+#define PCMK__XE_CIB_UPDATE_DIFF "cib_update_diff"
+
+// @COMPAT Deprecated since 2.1.7
+#define PCMK__XE_DIFF_ADDED "diff-added"
+
+// @COMPAT Deprecated since 2.1.7
+#define PCMK__XE_DIFF_REMOVED "diff-removed"
+
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XE_FAILED "failed"
+
+// @COMPAT Deprecated since 1.0.8 (commit 4cb100f)
+#define PCMK__XE_LIFETIME "lifetime"
+
+/* @COMPAT Deprecated since 2.0.0; alias for <clone> with PCMK_META_PROMOTABLE
+ * set to "true"
+ */
+#define PCMK__XE_PROMOTABLE_LEGACY "master"
+
+// @COMPAT Support for rkt is deprecated since 2.1.8
+#define PCMK__XE_RKT "rkt"
+
+// @COMPAT Deprecated since 1.1.12
+#define PCMK__XE_ROLE_REF "role_ref"
+
+
+/*
+ * XML attribute names used only by internal code
+ */
+
+#define PCMK__XA_ATTR_CLEAR_INTERVAL "attr_clear_interval"
+#define PCMK__XA_ATTR_CLEAR_OPERATION "attr_clear_operation"
+#define PCMK__XA_ATTR_DAMPENING "attr_dampening"
+#define PCMK__XA_ATTR_HOST "attr_host"
+#define PCMK__XA_ATTR_HOST_ID "attr_host_id"
+#define PCMK__XA_ATTR_IS_PRIVATE "attr_is_private"
+#define PCMK__XA_ATTR_IS_REMOTE "attr_is_remote"
+#define PCMK__XA_ATTR_NAME "attr_name"
+#define PCMK__XA_ATTR_REGEX "attr_regex"
+#define PCMK__XA_ATTR_RESOURCE "attr_resource"
+#define PCMK__XA_ATTR_SECTION "attr_section"
+#define PCMK__XA_ATTR_SET "attr_set"
+#define PCMK__XA_ATTR_SET_TYPE "attr_set_type"
+#define PCMK__XA_ATTR_SYNC_POINT "attr_sync_point"
+#define PCMK__XA_ATTR_USER "attr_user"
+#define PCMK__XA_ATTR_VALUE "attr_value"
+#define PCMK__XA_ATTR_VERSION "attr_version"
+#define PCMK__XA_ATTR_WRITER "attr_writer"
+#define PCMK__XA_ATTRD_IS_FORCE_WRITE "attrd_is_force_write"
+#define PCMK__XA_CALL_ID "call-id"
+#define PCMK__XA_CIB_CALLID "cib_callid"
+#define PCMK__XA_CIB_CALLOPT "cib_callopt"
+#define PCMK__XA_CIB_CLIENTID "cib_clientid"
+#define PCMK__XA_CIB_CLIENTNAME "cib_clientname"
+#define PCMK__XA_CIB_DELEGATED_FROM "cib_delegated_from"
+#define PCMK__XA_CIB_HOST "cib_host"
+#define PCMK__XA_CIB_ISREPLYTO "cib_isreplyto"
+#define PCMK__XA_CIB_NOTIFY_ACTIVATE "cib_notify_activate"
+#define PCMK__XA_CIB_NOTIFY_TYPE "cib_notify_type"
+#define PCMK__XA_CIB_OP "cib_op"
+#define PCMK__XA_CIB_PING_ID "cib_ping_id"
+#define PCMK__XA_CIB_RC "cib_rc"
+#define PCMK__XA_CIB_SCHEMA_MAX "cib_schema_max"
+#define PCMK__XA_CIB_SECTION "cib_section"
+#define PCMK__XA_CIB_UPDATE "cib_update"
+#define PCMK__XA_CIB_UPGRADE_RC "cib_upgrade_rc"
+#define PCMK__XA_CIB_USER "cib_user"
+#define PCMK__XA_CLIENT_NAME "client_name"
+#define PCMK__XA_CLIENT_UUID "client_uuid"
+#define PCMK__XA_CONFIG_ERRORS "config-errors"
+#define PCMK__XA_CONFIG_WARNINGS "config-warnings"
+#define PCMK__XA_CONFIRM "confirm"
+#define PCMK__XA_CONNECTION_HOST "connection_host"
+#define PCMK__XA_CONTENT "content"
+#define PCMK__XA_CRMD_STATE "crmd_state"
+#define PCMK__XA_CRM_HOST_TO "crm_host_to"
+#define PCMK__XA_CRM_LIMIT_MAX "crm-limit-max"
+#define PCMK__XA_CRM_LIMIT_MODE "crm-limit-mode"
+#define PCMK__XA_CRM_SUBSYSTEM "crm_subsystem"
+#define PCMK__XA_CRM_SYS_FROM "crm_sys_from"
+#define PCMK__XA_CRM_SYS_TO "crm_sys_to"
+#define PCMK__XA_CRM_TASK "crm_task"
+#define PCMK__XA_CRM_TGRAPH_IN "crm-tgraph-in"
+#define PCMK__XA_CRM_USER "crm_user"
+#define PCMK__XA_DC_LEAVING "dc-leaving"
+#define PCMK__XA_DIGEST "digest"
+#define PCMK__XA_ELECTION_AGE_SEC "election-age-sec"
+#define PCMK__XA_ELECTION_AGE_NANO_SEC "election-age-nano-sec"
+#define PCMK__XA_ELECTION_ID "election-id"
+#define PCMK__XA_ELECTION_OWNER "election-owner"
+#define PCMK__XA_GRANTED "granted"
+#define PCMK__XA_GRAPH_ERRORS "graph-errors"
+#define PCMK__XA_GRAPH_WARNINGS "graph-warnings"
+#define PCMK__XA_HIDDEN "hidden"
+#define PCMK__XA_HTTP_EQUIV "http-equiv"
+#define PCMK__XA_IN_CCM "in_ccm"
+#define PCMK__XA_JOIN "join"
+#define PCMK__XA_JOIN_ID "join_id"
+#define PCMK__XA_LINE "line"
+#define PCMK__XA_LONG_ID "long-id"
+#define PCMK__XA_LRMD_ALERT_ID "lrmd_alert_id"
+#define PCMK__XA_LRMD_ALERT_PATH "lrmd_alert_path"
+#define PCMK__XA_LRMD_CALLID "lrmd_callid"
+#define PCMK__XA_LRMD_CALLOPT "lrmd_callopt"
+#define PCMK__XA_LRMD_CLASS "lrmd_class"
+#define PCMK__XA_LRMD_CLIENTID "lrmd_clientid"
+#define PCMK__XA_LRMD_CLIENTNAME "lrmd_clientname"
+#define PCMK__XA_LRMD_EXEC_OP_STATUS "lrmd_exec_op_status"
+#define PCMK__XA_LRMD_EXEC_RC "lrmd_exec_rc"
+#define PCMK__XA_LRMD_EXEC_TIME "lrmd_exec_time"
+#define PCMK__XA_LRMD_IPC_CLIENT "lrmd_ipc_client"
+#define PCMK__XA_LRMD_IPC_MSG_FLAGS "lrmd_ipc_msg_flags"
+#define PCMK__XA_LRMD_IPC_MSG_ID "lrmd_ipc_msg_id"
+#define PCMK__XA_LRMD_IPC_OP "lrmd_ipc_op"
+#define PCMK__XA_LRMD_IPC_SERVER "lrmd_ipc_server"
+#define PCMK__XA_LRMD_IPC_SESSION "lrmd_ipc_session"
+#define PCMK__XA_LRMD_IPC_USER "lrmd_ipc_user"
+#define PCMK__XA_LRMD_IS_IPC_PROVIDER "lrmd_is_ipc_provider"
+#define PCMK__XA_LRMD_OP "lrmd_op"
+#define PCMK__XA_LRMD_ORIGIN "lrmd_origin"
+#define PCMK__XA_LRMD_PROTOCOL_VERSION "lrmd_protocol_version"
+#define PCMK__XA_LRMD_PROVIDER "lrmd_provider"
+#define PCMK__XA_LRMD_QUEUE_TIME "lrmd_queue_time"
+#define PCMK__XA_LRMD_RC "lrmd_rc"
+#define PCMK__XA_LRMD_RCCHANGE_TIME "lrmd_rcchange_time"
+#define PCMK__XA_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id"
+#define PCMK__XA_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type"
+#define PCMK__XA_LRMD_RSC_ACTION "lrmd_rsc_action"
+#define PCMK__XA_LRMD_RSC_DELETED "lrmd_rsc_deleted"
+#define PCMK__XA_LRMD_RSC_EXIT_REASON "lrmd_rsc_exit_reason"
+#define PCMK__XA_LRMD_RSC_ID "lrmd_rsc_id"
+#define PCMK__XA_LRMD_RSC_INTERVAL "lrmd_rsc_interval"
+#define PCMK__XA_LRMD_RSC_OUTPUT "lrmd_rsc_output"
+#define PCMK__XA_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay"
+#define PCMK__XA_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str"
+#define PCMK__XA_LRMD_RUN_TIME "lrmd_run_time"
+#define PCMK__XA_LRMD_TIMEOUT "lrmd_timeout"
+#define PCMK__XA_LRMD_TYPE "lrmd_type"
+#define PCMK__XA_LRMD_WATCHDOG "lrmd_watchdog"
+#define PCMK__XA_MAJOR_VERSION "major_version"
+#define PCMK__XA_MINOR_VERSION "minor_version"
+#define PCMK__XA_MODE "mode"
+#define PCMK__XA_MOON "moon"
+#define PCMK__XA_NAMESPACE "namespace"
+#define PCMK__XA_NODE_FENCED "node_fenced"
+#define PCMK__XA_NODE_IN_MAINTENANCE "node_in_maintenance"
+#define PCMK__XA_NODE_START_STATE "node_start_state"
+#define PCMK__XA_NODE_STATE "node_state"
+#define PCMK__XA_OP_DIGEST "op-digest"
+#define PCMK__XA_OP_FORCE_RESTART "op-force-restart"
+#define PCMK__XA_OP_RESTART_DIGEST "op-restart-digest"
+#define PCMK__XA_OP_SECURE_DIGEST "op-secure-digest"
+#define PCMK__XA_OP_SECURE_PARAMS "op-secure-params"
+#define PCMK__XA_OP_STATUS "op-status"
+#define PCMK__XA_OPERATION_KEY "operation_key"
+#define PCMK__XA_ORIGINAL_CIB_OP "original_cib_op"
+#define PCMK__XA_PACEMAKERD_STATE "pacemakerd_state"
+#define PCMK__XA_PASSWORD "password"
+#define PCMK__XA_PRIORITY "priority"
+#define PCMK__XA_RC_CODE "rc-code"
+#define PCMK__XA_REAP "reap"
+
+/* Actions to be executed on Pacemaker Remote nodes are routed through the
+ * controller on the cluster node hosting the remote connection. That cluster
+ * node is considered the router node for the action.
+ */
+#define PCMK__XA_ROUTER_NODE "router_node"
+
+#define PCMK__XA_RSC_ID "rsc-id"
+#define PCMK__XA_RSC_PROVIDES "rsc_provides"
+#define PCMK__XA_SCHEMA "schema"
+#define PCMK__XA_SCHEMAS "schemas"
+#define PCMK__XA_SET "set"
+#define PCMK__XA_SRC "src"
+#define PCMK__XA_ST_ACTION_DISALLOWED "st_action_disallowed"
+#define PCMK__XA_ST_ACTION_TIMEOUT "st_action_timeout"
+#define PCMK__XA_ST_AVAILABLE_DEVICES "st-available-devices"
+#define PCMK__XA_ST_CALLID "st_callid"
+#define PCMK__XA_ST_CALLOPT "st_callopt"
+#define PCMK__XA_ST_CLIENTID "st_clientid"
+#define PCMK__XA_ST_CLIENTNAME "st_clientname"
+#define PCMK__XA_ST_CLIENTNODE "st_clientnode"
+#define PCMK__XA_ST_DATE "st_date"
+#define PCMK__XA_ST_DATE_NSEC "st_date_nsec"
+#define PCMK__XA_ST_DELAY "st_delay"
+#define PCMK__XA_ST_DELAY_BASE "st_delay_base"
+#define PCMK__XA_ST_DELAY_MAX "st_delay_max"
+#define PCMK__XA_ST_DELEGATE "st_delegate"
+#define PCMK__XA_ST_DEVICE_ACTION "st_device_action"
+#define PCMK__XA_ST_DEVICE_ID "st_device_id"
+#define PCMK__XA_ST_DEVICE_SUPPORT_FLAGS "st_device_support_flags"
+#define PCMK__XA_ST_DIFFERENTIAL "st_differential"
+#define PCMK__XA_ST_MONITOR_VERIFIED "st_monitor_verified"
+#define PCMK__XA_ST_NOTIFY_ACTIVATE "st_notify_activate"
+#define PCMK__XA_ST_NOTIFY_DEACTIVATE "st_notify_deactivate"
+#define PCMK__XA_ST_OP "st_op"
+#define PCMK__XA_ST_OP_MERGED "st_op_merged"
+#define PCMK__XA_ST_ORIGIN "st_origin"
+#define PCMK__XA_ST_OUTPUT "st_output"
+#define PCMK__XA_ST_RC "st_rc"
+#define PCMK__XA_ST_REMOTE_OP "st_remote_op"
+#define PCMK__XA_ST_REMOTE_OP_RELAY "st_remote_op_relay"
+#define PCMK__XA_ST_REQUIRED "st_required"
+#define PCMK__XA_ST_STATE "st_state"
+#define PCMK__XA_ST_TARGET "st_target"
+#define PCMK__XA_ST_TIMEOUT "st_timeout"
+#define PCMK__XA_ST_TOLERANCE "st_tolerance"
+#define PCMK__XA_SUBT "subt" // subtype
+#define PCMK__XA_T "t" // type
+#define PCMK__XA_TRANSITION_KEY "transition-key"
+#define PCMK__XA_TRANSITION_MAGIC "transition-magic"
+#define PCMK__XA_UPTIME "uptime"
+
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XA_CIB_OBJECT "cib_object"
+
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XA_CIB_OBJECT_TYPE "cib_object_type"
+
+// @COMPAT Deprecated since 1.1.12; used with legacy CIB updates
+#define PCMK__XA_CIB_LOCAL_NOTIFY_ID "cib_local_notify_id"
+
+// @COMPAT Used only with v1 patchsets
+#define PCMK__XA_CRM_DIFF_MARKER "__crm_diff_marker__"
+
+// @COMPAT Deprecated since 2.1.5
+#define PCMK__XA_FIRST_INSTANCE "first-instance"
+
+// @COMPAT Deprecated since 2.1.7
+#define PCMK__XA_ORDERING "ordering"
+
+// @COMPAT Deprecated alias for PCMK_XA_PROMOTED_MAX since 2.0.0
+#define PCMK__XA_PROMOTED_MAX_LEGACY "masters"
+
+// @COMPAT Deprecated alias for PCMK_XA_PROMOTED_ONLY since 2.0.0
+#define PCMK__XA_PROMOTED_ONLY_LEGACY "master_only"
+
+// @COMPAT Deprecated since 1.1.12
+#define PCMK__XA_REF "ref"
+
+// @COMPAT Deprecated since 2.1.6
+#define PCMK__XA_REPLACE "replace"
+
+// @COMPAT Deprecated alias for \c PCMK_XA_AUTOMATIC since 1.1.14
+#define PCMK__XA_REQUIRED "required"
+
+// @COMPAT Deprecated since 2.1.5
+#define PCMK__XA_RSC_INSTANCE "rsc-instance"
+
+// @COMPAT Deprecated since 2.1.5
+#define PCMK__XA_THEN_INSTANCE "then-instance"
+
+// @COMPAT Deprecated since 2.1.5
+#define PCMK__XA_WITH_RSC_INSTANCE "with-rsc-instance"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
diff --git a/include/crm/compatibility.h b/include/crm/compatibility.h
index f8502cc..b1a3036 100644
--- a/include/crm/compatibility.h
+++ b/include/crm/compatibility.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -9,7 +9,8 @@
#ifndef PCMK__CRM_COMPATIBILITY__H
# define PCMK__CRM_COMPATIBILITY__H
-#include <crm/msg_xml.h>
+#include <crm/msg_xml_compat.h> // PCMK_XE_PROMOTABLE_LEGACY
+#include <crm/common/xml.h>
#include <crm/pengine/pe_types.h> // enum pe_obj_types
#ifdef __cplusplus
@@ -127,7 +128,7 @@ extern "C" {
#define LOG_DEBUG_6 LOG_TRACE
#define LRMD_OP_RSC_CHK_REG "lrmd_rsc_check_register"
#define MAX_IPC_FAIL 5
-#define NAME(x) crm_element_value(x, XML_NVPAIR_ATTR_NAME)
+#define NAME(x) crm_element_value(x, PCMK_XA_NAME)
#define MSG_LOG 1
#define PE_OBJ_T_NATIVE "native"
#define PE_OBJ_T_GROUP "group"
@@ -135,7 +136,7 @@ extern "C" {
#define PE_OBJ_T_MASTER "master"
#define SERVICE_SCRIPT "/sbin/service"
#define SOCKET_LEN 1024
-#define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP)
+#define TSTAMP(x) crm_element_value(x, PCMK_XA_CRM_TIMESTAMP)
#define XML_ATTR_TAGNAME F_XML_TAGNAME
#define XML_ATTR_FILTER_TYPE "type-filter"
#define XML_ATTR_FILTER_ID "id-filter"
@@ -145,14 +146,14 @@ extern "C" {
#define XML_MSG_TAG_DATA "msg_data"
#define XML_FAIL_TAG_RESOURCE "failed_resource"
#define XML_FAILRES_ATTR_RESID "resource_id"
-#define XML_FAILRES_ATTR_REASON "reason"
+#define XML_FAILRES_ATTR_REASON PCMK_XA_REASON
#define XML_FAILRES_ATTR_RESSTATUS "resource_status"
-#define XML_ATTR_RESULT "result"
+#define XML_ATTR_RESULT PCMK_XA_RESULT
#define XML_ATTR_SECTION "section"
#define XML_CIB_TAG_DOMAIN "domain"
#define XML_CIB_TAG_CONSTRAINT "constraint"
#define XML_RSC_ATTR_STATE "clone-state"
-#define XML_RSC_ATTR_PRIORITY "priority"
+#define XML_RSC_ATTR_PRIORITY PCMK_META_PRIORITY
#define XML_OP_ATTR_DEPENDENT "dependent-on"
#define XML_LRM_TAG_AGENTS "lrm_agents"
#define XML_LRM_TAG_AGENT "lrm_agent"
@@ -163,11 +164,11 @@ extern "C" {
#define XML_CIB_ATTR_STONITH "stonith"
#define XML_CIB_ATTR_STANDBY "standby"
#define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled"
-#define XML_RULE_ATTR_RESULT "result"
+#define XML_RULE_ATTR_RESULT PCMK_XA_RESULT
#define XML_NODE_ATTR_STATE "state"
#define XML_ATTR_LRM_PROBE "lrm-is-probe"
#define XML_ATTR_TE_ALLOWFAIL "op_allow_fail"
-#define VALUE(x) crm_element_value(x, XML_NVPAIR_ATTR_VALUE)
+#define VALUE(x) crm_element_value(x, PCMK_XA_VALUE)
#define action_wrapper_s pe_action_wrapper_s
#define add_cib_op_callback(cib, id, flag, data, fn) do { \
cib->cmds->register_callback(cib, id, 120, flag, data, #fn, fn); \
@@ -201,17 +202,17 @@ extern "C" {
static inline enum pe_obj_types
get_resource_type(const char *name)
{
- if (safe_str_eq(name, XML_CIB_TAG_RESOURCE)) {
+ if (safe_str_eq(name, PCMK_XE_PRIMITIVE)) {
return pcmk_rsc_variant_primitive;
- } else if (safe_str_eq(name, XML_CIB_TAG_GROUP)) {
+ } else if (safe_str_eq(name, PCMK_XE_GROUP)) {
return pcmk_rsc_variant_group;
- } else if (safe_str_eq(name, XML_CIB_TAG_INCARNATION)
+ } else if (safe_str_eq(name, PCMK_XE_CLONE)
|| safe_str_eq(name, PCMK_XE_PROMOTABLE_LEGACY)) {
return pcmk_rsc_variant_clone;
- } else if (safe_str_eq(name, XML_CIB_TAG_CONTAINER)) {
+ } else if (safe_str_eq(name, PCMK_XE_BUNDLE)) {
return pcmk_rsc_variant_bundle;
}
@@ -223,13 +224,13 @@ get_resource_typename(enum pe_obj_types type)
{
switch (type) {
case pcmk_rsc_variant_primitive:
- return XML_CIB_TAG_RESOURCE;
+ return PCMK_XE_PRIMITIVE;
case pcmk_rsc_variant_group:
- return XML_CIB_TAG_GROUP;
+ return PCMK_XE_GROUP;
case pcmk_rsc_variant_clone:
- return XML_CIB_TAG_INCARNATION;
+ return PCMK_XE_CLONE;
case pcmk_rsc_variant_bundle:
- return XML_CIB_TAG_CONTAINER;
+ return PCMK_XE_BUNDLE;
case pcmk_rsc_variant_unknown:
return "unknown";
}
diff --git a/include/crm/crm.h b/include/crm/crm.h
index aecfcc8..2ef84d3 100644
--- a/include/crm/crm.h
+++ b/include/crm/crm.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -19,6 +19,8 @@
# include <libxml/tree.h>
+#include <crm/common/options.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -67,7 +69,7 @@ extern "C" {
* >=3.2.0: DC supports PCMK_EXEC_INVALID and PCMK_EXEC_NOT_CONNECTED
* >=3.19.0: DC supports PCMK__CIB_REQUEST_COMMIT_TRANSACT
*/
-# define CRM_FEATURE_SET "3.19.0"
+# define CRM_FEATURE_SET "3.19.5"
/* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
* recipient of a CPG message. This imposes an arbitrary limit on cluster node
@@ -78,25 +80,9 @@ extern "C" {
# define CRM_META "CRM_meta"
+// NOTE: sbd (as of at least 1.5.2) uses this
extern char *crm_system_name;
-// How we represent "infinite" scores
-# define CRM_SCORE_INFINITY 1000000
-# define CRM_INFINITY_S "INFINITY"
-# define CRM_PLUS_INFINITY_S "+" CRM_INFINITY_S
-# define CRM_MINUS_INFINITY_S "-" CRM_INFINITY_S
-
-/* @COMPAT API < 2.0.0 Deprecated "infinity" aliases
- *
- * INFINITY might be defined elsewhere (e.g. math.h), so undefine it first.
- * This, of course, complicates any attempt to use the other definition in any
- * code that includes this header.
- */
-# undef INFINITY
-# define INFINITY_S "INFINITY"
-# define MINUS_INFINITY_S "-INFINITY"
-# define INFINITY 1000000
-
/* Sub-systems */
# define CRM_SYSTEM_DC "dc"
#define CRM_SYSTEM_DCIB "dcib" // Primary instance of CIB manager
@@ -138,7 +124,6 @@ extern char *crm_system_name;
# define CRM_OP_HELLO "hello"
# define CRM_OP_PECALC "pe_calc"
# define CRM_OP_QUIT "quit"
-# define CRM_OP_LOCAL_SHUTDOWN "start_shutdown"
# define CRM_OP_SHUTDOWN_REQ "req_shutdown"
# define CRM_OP_SHUTDOWN PCMK_ACTION_DO_SHUTDOWN
# define CRM_OP_REGISTER "register"
diff --git a/include/crm/crm_compat.h b/include/crm/crm_compat.h
index bfe1098..a5f2b6e 100644
--- a/include/crm/crm_compat.h
+++ b/include/crm/crm_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,6 +14,7 @@
#include <glib.h>
#include <crm/common/actions.h>
+#include <crm/common/scores.h>
#ifdef __cplusplus
extern "C" {
@@ -34,6 +35,33 @@ extern "C" {
//! \deprecated This defined constant will be removed in a future release
#define MAX_IPC_DELAY 120
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use PCMK_SCORE_INFINITY instead
+#define CRM_SCORE_INFINITY PCMK_SCORE_INFINITY
+
+/* INFINITY might be defined elsewhere (such as math.h), so undefine it first.
+ * This, of course, complicates any attempt to use the other definition in any
+ * code that includes this header.
+ */
+//! \deprecated Use PCMK_SCORE_INFINITY instead
+#undef INFINITY
+#define INFINITY PCMK_SCORE_INFINITY
+
+//! \deprecated Use PCMK_VALUE_INFINITY instead
+#define CRM_INFINITY_S PCMK_VALUE_INFINITY
+
+//! \deprecated Use PCMK_VALUE_MINUS_INFINITY instead
+#define CRM_MINUS_INFINITY_S PCMK_VALUE_MINUS_INFINITY
+
+//! \deprecated Use PCMK_VALUE_PLUS_INFINITY instead
+#define CRM_PLUS_INFINITY_S PCMK_VALUE_PLUS_INFINITY
+
+//! \deprecated Use PCMK_VALUE_INFINITY instead
+#define INFINITY_S "INFINITY"
+
+//! \deprecated Use PCMK_VALUE_MINUS_INFINITY instead
+#define MINUS_INFINITY_S "-INFINITY"
+
//! \deprecated Use PCMK_ACTION_STONITH instead
#define CRM_OP_FENCE PCMK_ACTION_STONITH
@@ -154,6 +182,9 @@ extern "C" {
//! \deprecated Use PCMK_ACTION_STOPPED instead
#define RSC_STOPPED PCMK_ACTION_STOPPED
+//! \deprecated Do not use
+#define CRM_OP_LOCAL_SHUTDOWN "start_shutdown"
+
//!@{
//! \deprecated This macro will be removed in a future release
@@ -168,6 +199,7 @@ extern "C" {
// This ends the doxygen deprecation comment
//!@}
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Use GList * instead
typedef GList *GListPtr;
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
index 492c035..414086d 100644
--- a/include/crm/fencing/internal.h
+++ b/include/crm/fencing/internal.h
@@ -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.
*
@@ -102,66 +102,7 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
# define ST_LEVEL_MAX 10
-# define F_STONITH_CLIENTID "st_clientid"
-# define F_STONITH_CALLOPTS "st_callopt"
-# define F_STONITH_CALLID "st_callid"
-# define F_STONITH_CALLDATA "st_calldata"
-# define F_STONITH_OPERATION "st_op"
-# define F_STONITH_TARGET "st_target"
-# define F_STONITH_REMOTE_OP_ID "st_remote_op"
-# define F_STONITH_REMOTE_OP_ID_RELAY "st_remote_op_relay"
-# define F_STONITH_RC "st_rc"
-# define F_STONITH_OUTPUT "st_output"
-/*! Timeout period per a device execution */
-# define F_STONITH_TIMEOUT "st_timeout"
-# define F_STONITH_TOLERANCE "st_tolerance"
-# define F_STONITH_DELAY "st_delay"
-/*! Action specific timeout period returned in query of fencing devices. */
-# define F_STONITH_ACTION_TIMEOUT "st_action_timeout"
-/*! Host in query result is not allowed to run this action */
-# define F_STONITH_ACTION_DISALLOWED "st_action_disallowed"
-/*! Maximum of random fencing delay for a device */
-# define F_STONITH_DELAY_MAX "st_delay_max"
-/*! Base delay used for a fencing delay */
-# define F_STONITH_DELAY_BASE "st_delay_base"
-/*! Has this device been verified using a monitor type
- * operation (monitor, list, status) */
-# define F_STONITH_DEVICE_VERIFIED "st_monitor_verified"
-/*! device is required for this action */
-# define F_STONITH_DEVICE_REQUIRED "st_required"
-/*! number of available devices in query result */
-# define F_STONITH_AVAILABLE_DEVICES "st-available-devices"
-# define F_STONITH_CALLBACK_TOKEN "st_async_id"
-# define F_STONITH_CLIENTNAME "st_clientname"
-# define F_STONITH_CLIENTNODE "st_clientnode"
-# define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate"
-# define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate"
-# define F_STONITH_DELEGATE "st_delegate"
-# define F_STONITH_DEVICE_SUPPORT_FLAGS "st_device_support_flags"
-/*! The node initiating the stonith operation. If an operation
- * is relayed, this is the last node the operation lands on. When
- * in standalone mode, origin is the client's id that originated the
- * operation. */
-# define F_STONITH_ORIGIN "st_origin"
-# define F_STONITH_HISTORY_LIST "st_history"
-# define F_STONITH_DATE "st_date"
-# define F_STONITH_DATE_NSEC "st_date_nsec"
-# define F_STONITH_STATE "st_state"
-# define F_STONITH_ACTIVE "st_active"
-# define F_STONITH_DIFFERENTIAL "st_differential"
-
-# define F_STONITH_DEVICE "st_device_id"
-# define F_STONITH_ACTION "st_device_action"
-# define F_STONITH_MERGED "st_op_merged"
-
-# define T_STONITH_NG "stonith-ng"
-# define T_STONITH_REPLY "st-reply"
-/*! For async operations, an event from the server containing
- * the total amount of time the server is allowing for the operation
- * to take place is returned to the client. */
-# define T_STONITH_TIMEOUT_VALUE "st-async-timeout-value"
-# define T_STONITH_NOTIFY "st_notify"
-
+// @COMPAT Deprecated since 1.1.17 (and see T773 to drop it)
# define STONITH_ATTR_ACTION_OP "action"
# define STONITH_OP_EXEC "st_execute"
@@ -174,6 +115,9 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
# define STONITH_OP_FENCE_HISTORY "st_fence_history"
# define STONITH_OP_LEVEL_ADD "st_level_add"
# define STONITH_OP_LEVEL_DEL "st_level_remove"
+# define STONITH_OP_NOTIFY "st_notify"
+# define STONITH_OP_POKE "poke"
+
# define STONITH_WATCHDOG_AGENT "fence_watchdog"
/* Don't change 2 below as it would break rolling upgrade */
diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h
index 0c5a40b..9b3f698 100644
--- a/include/crm/lrmd.h
+++ b/include/crm/lrmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2022 the Pacemaker project contributors
+ * Copyright 2012-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -33,12 +33,27 @@ typedef struct lrmd_key_value_s {
struct lrmd_key_value_s *next;
} lrmd_key_value_t;
-/* This should be bumped every time there is an incompatible change that
- * prevents older clients from connecting to this version of the server.
+/* The major version should be bumped every time there is an incompatible
+ * change that prevents older clients from connecting to this version of
+ * the server. The minor version indicates feature support.
+ *
+ * Protocol Pacemaker Significant changes
+ * -------- --------- -------------------
+ * 1.2 2.1.8 PCMK__CIB_REQUEST_SCHEMAS
*/
-#define LRMD_PROTOCOL_VERSION "1.1"
+#define LRMD_PROTOCOL_VERSION "1.2"
+
+#define LRMD_SUPPORTS_SCHEMA_XFER(x) (compare_version((x), "1.2") >= 0)
-/* This is the version that the client version will actually be compared
+/* The major protocol version the client and server both need to support for
+ * the connection to be successful. This should only ever be the major
+ * version - not a complete version number.
+ */
+#define LRMD_COMPATIBLE_PROTOCOL "1"
+
+/* \deprecated Do not use (will be removed in a future release)
+ *
+ * This is the version that the client version will actually be compared
* against. This should be identical to LRMD_PROTOCOL_VERSION. However, we
* accidentally bumped LRMD_PROTOCOL_VERSION in 6424a647 (1.1.15) when we didn't
* need to, so for now it's different. If we ever have a truly incompatible
@@ -52,46 +67,6 @@ typedef struct lrmd_key_value_s {
#define DEFAULT_REMOTE_PORT 3121
#define DEFAULT_REMOTE_USERNAME "lrmd"
-#define F_LRMD_OPERATION "lrmd_op"
-#define F_LRMD_CLIENTNAME "lrmd_clientname"
-#define F_LRMD_IS_IPC_PROVIDER "lrmd_is_ipc_provider"
-#define F_LRMD_CLIENTID "lrmd_clientid"
-#define F_LRMD_PROTOCOL_VERSION "lrmd_protocol_version"
-#define F_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type"
-#define F_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id"
-#define F_LRMD_CALLBACK_TOKEN "lrmd_async_id"
-#define F_LRMD_CALLID "lrmd_callid"
-#define F_LRMD_CALLOPTS "lrmd_callopt"
-#define F_LRMD_CALLDATA "lrmd_calldata"
-#define F_LRMD_RC "lrmd_rc"
-#define F_LRMD_EXEC_RC "lrmd_exec_rc"
-#define F_LRMD_OP_STATUS "lrmd_exec_op_status"
-#define F_LRMD_TIMEOUT "lrmd_timeout"
-#define F_LRMD_WATCHDOG "lrmd_watchdog"
-#define F_LRMD_CLASS "lrmd_class"
-#define F_LRMD_PROVIDER "lrmd_provider"
-#define F_LRMD_TYPE "lrmd_type"
-#define F_LRMD_ORIGIN "lrmd_origin"
-
-#define F_LRMD_RSC_RUN_TIME "lrmd_run_time"
-#define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time"
-#define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time"
-#define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time"
-
-#define F_LRMD_RSC_ID "lrmd_rsc_id"
-#define F_LRMD_RSC_ACTION "lrmd_rsc_action"
-#define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str"
-#define F_LRMD_RSC_OUTPUT "lrmd_rsc_output"
-#define F_LRMD_RSC_EXIT_REASON "lrmd_rsc_exit_reason"
-#define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay"
-#define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval"
-#define F_LRMD_RSC_DELETED "lrmd_rsc_deleted"
-#define F_LRMD_RSC "lrmd_rsc"
-
-#define F_LRMD_ALERT_ID "lrmd_alert_id"
-#define F_LRMD_ALERT_PATH "lrmd_alert_path"
-#define F_LRMD_ALERT "lrmd_alert"
-
#define LRMD_OP_RSC_REG "lrmd_rsc_register"
#define LRMD_OP_RSC_EXEC "lrmd_rsc_exec"
#define LRMD_OP_RSC_CANCEL "lrmd_rsc_cancel"
@@ -112,21 +87,6 @@ typedef struct lrmd_key_value_s {
#define LRMD_IPC_OP_SHUTDOWN_REQ "shutdown_req"
#define LRMD_IPC_OP_SHUTDOWN_ACK "shutdown_ack"
#define LRMD_IPC_OP_SHUTDOWN_NACK "shutdown_nack"
-
-#define F_LRMD_IPC_OP "lrmd_ipc_op"
-#define F_LRMD_IPC_IPC_SERVER "lrmd_ipc_server"
-#define F_LRMD_IPC_SESSION "lrmd_ipc_session"
-#define F_LRMD_IPC_CLIENT "lrmd_ipc_client"
-#define F_LRMD_IPC_USER "lrmd_ipc_user"
-#define F_LRMD_IPC_MSG "lrmd_ipc_msg"
-#define F_LRMD_IPC_MSG_ID "lrmd_ipc_msg_id"
-#define F_LRMD_IPC_MSG_FLAGS "lrmd_ipc_msg_flags"
-
-#define T_LRMD "lrmd"
-#define T_LRMD_REPLY "lrmd_reply"
-#define T_LRMD_NOTIFY "lrmd_notify"
-#define T_LRMD_IPC_PROXY "lrmd_ipc_proxy"
-#define T_LRMD_RSC_OP "lrmd_rsc_op"
/* *INDENT-ON* */
/*!
@@ -554,6 +514,10 @@ lrmd_event_type2str(enum lrmd_callback_event type)
return "unknown";
}
+#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+#include <crm/lrmd_compat.h>
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/lrmd_compat.h b/include/crm/lrmd_compat.h
new file mode 100644
index 0000000..98461e0
--- /dev/null
+++ b/include/crm/lrmd_compat.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2012-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.
+ */
+
+#ifndef PCMK__CRM_LRMD_COMPAT__H
+# define PCMK__CRM_LRMD_COMPAT__H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Deprecated executor utilities
+ * \ingroup core
+ * \deprecated Do not include this header directly. The utilities in this
+ * header, and the header itself, will be removed in a future
+ * release.
+ */
+
+//! \deprecated Do not use
+#define F_LRMD_OPERATION "lrmd_op"
+
+//! \deprecated Do not use
+#define F_LRMD_CLIENTNAME "lrmd_clientname"
+
+//! \deprecated Do not use
+#define F_LRMD_CALLBACK_TOKEN "lrmd_async_id"
+
+//! \deprecated Do not use
+#define F_LRMD_IS_IPC_PROVIDER "lrmd_is_ipc_provider"
+
+//! \deprecated Do not use
+#define F_LRMD_CLIENTID "lrmd_clientid"
+
+//! \deprecated Do not use
+#define F_LRMD_PROTOCOL_VERSION "lrmd_protocol_version"
+
+//! \deprecated Do not use
+#define F_LRMD_REMOTE_MSG_TYPE "lrmd_remote_msg_type"
+
+//! \deprecated Do not use
+#define F_LRMD_REMOTE_MSG_ID "lrmd_remote_msg_id"
+
+//! \deprecated Do not use
+#define F_LRMD_CALLID "lrmd_callid"
+
+//! \deprecated Do not use
+#define F_LRMD_CALLOPTS "lrmd_callopt"
+
+//! \deprecated Do not use
+#define F_LRMD_CALLDATA "lrmd_calldata"
+
+//! \deprecated Do not use
+#define F_LRMD_RC "lrmd_rc"
+
+//! \deprecated Do not use
+#define F_LRMD_EXEC_RC "lrmd_exec_rc"
+
+//! \deprecated Do not use
+#define F_LRMD_OP_STATUS "lrmd_exec_op_status"
+
+//! \deprecated Do not use
+#define F_LRMD_TIMEOUT "lrmd_timeout"
+
+//! \deprecated Do not use
+#define F_LRMD_WATCHDOG "lrmd_watchdog"
+
+//! \deprecated Do not use
+#define F_LRMD_CLASS "lrmd_class"
+
+//! \deprecated Do not use
+#define F_LRMD_PROVIDER "lrmd_provider"
+
+//! \deprecated Do not use
+#define F_LRMD_TYPE "lrmd_type"
+
+//! \deprecated Do not use
+#define F_LRMD_ORIGIN "lrmd_origin"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_RUN_TIME "lrmd_run_time"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_ID "lrmd_rsc_id"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_ACTION "lrmd_rsc_action"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_OUTPUT "lrmd_rsc_output"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_EXIT_REASON "lrmd_rsc_exit_reason"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC_DELETED "lrmd_rsc_deleted"
+
+//! \deprecated Do not use
+#define F_LRMD_RSC "lrmd_rsc"
+
+//! \deprecated Do not use
+#define F_LRMD_ALERT_ID "lrmd_alert_id"
+
+//! \deprecated Do not use
+#define F_LRMD_ALERT_PATH "lrmd_alert_path"
+
+//! \deprecated Do not use
+#define F_LRMD_ALERT "lrmd_alert"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_OP "lrmd_ipc_op"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_IPC_SERVER "lrmd_ipc_server"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_SESSION "lrmd_ipc_session"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_CLIENT "lrmd_ipc_client"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_USER "lrmd_ipc_user"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_MSG "lrmd_ipc_msg"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_MSG_ID "lrmd_ipc_msg_id"
+
+//! \deprecated Do not use
+#define F_LRMD_IPC_MSG_FLAGS "lrmd_ipc_msg_flags"
+
+//! \deprecated Do not use
+#define T_LRMD "lrmd"
+
+//! \deprecated Do not use
+#define T_LRMD_REPLY "lrmd_reply"
+
+//! \deprecated Do not use
+#define T_LRMD_NOTIFY "lrmd_notify"
+
+//! \deprecated Do not use
+#define T_LRMD_IPC_PROXY "lrmd_ipc_proxy"
+
+//! \deprecated Do not use
+#define T_LRMD_RSC_OP "lrmd_rsc_op"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_LRMD_COMPAT__H
diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h
index c616182..4a82ab3 100644
--- a/include/crm/msg_xml.h
+++ b/include/crm/msg_xml.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,471 +10,10 @@
#ifndef PCMK__CRM_MSG_XML__H
# define PCMK__CRM_MSG_XML__H
-# include <crm/common/xml.h>
-
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/msg_xml_compat.h>
+#else
+#error Include xml.h instead of msg_xml.h
#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* This file defines constants for various XML syntax (mainly element and
- * attribute names).
- *
- * For consistency, new constants should start with "PCMK_", followed by "XE"
- * for XML element names, "XA" for XML attribute names, and "META" for meta
- * attribute names. Old names that don't follow this policy should eventually be
- * deprecated and replaced with names that do.
- */
-
-/*
- * XML elements
- */
-
-#define PCMK_XE_DATE_EXPRESSION "date_expression"
-#define PCMK_XE_OP_EXPRESSION "op_expression"
-
-/* This has been deprecated as a CIB element (an alias for <clone> with
- * "promotable" set to "true") since 2.0.0.
- */
-#define PCMK_XE_PROMOTABLE_LEGACY "master"
-
-#define PCMK_XE_RSC_EXPRESSION "rsc_expression"
-
-
-/*
- * XML attributes
- */
-
-#define PCMK_XA_FORMAT "format"
-
-/* These have been deprecated as CIB <clone> element attributes (aliases for
- * "promoted-max" and "promoted-node-max") since 2.0.0.
- */
-#define PCMK_XA_PROMOTED_MAX_LEGACY "master-max"
-#define PCMK_XA_PROMOTED_NODE_MAX_LEGACY "master-node-max"
-
-
-/*
- * Meta attributes
- */
-
-#define PCMK_META_CLONE_MAX "clone-max"
-#define PCMK_META_CLONE_MIN "clone-min"
-#define PCMK_META_CLONE_NODE_MAX "clone-node-max"
-#define PCMK_META_ENABLED "enabled"
-#define PCMK_META_FAILURE_TIMEOUT "failure-timeout"
-#define PCMK_META_MIGRATION_THRESHOLD "migration-threshold"
-#define PCMK_META_PROMOTED_MAX "promoted-max"
-#define PCMK_META_PROMOTED_NODE_MAX "promoted-node-max"
-
-
-/*
- * Older constants that don't follow current naming
- */
-
-# ifndef F_ORIG
-# define F_ORIG "src"
-# endif
-
-# ifndef F_SEQ
-# define F_SEQ "seq"
-# endif
-
-# ifndef F_SUBTYPE
-# define F_SUBTYPE "subt"
-# endif
-
-# ifndef F_TYPE
-# define F_TYPE "t"
-# endif
-
-# ifndef F_CLIENTNAME
-# define F_CLIENTNAME "cn"
-# endif
-
-# ifndef F_XML_TAGNAME
-# define F_XML_TAGNAME "__name__"
-# endif
-
-# ifndef T_CRM
-# define T_CRM "crmd"
-# endif
-
-# ifndef T_ATTRD
-# define T_ATTRD "attrd"
-# endif
-
-# define CIB_OPTIONS_FIRST "cib-bootstrap-options"
-
-# define F_CRM_DATA "crm_xml"
-# define F_CRM_TASK "crm_task"
-# define F_CRM_HOST_TO "crm_host_to"
-# define F_CRM_MSG_TYPE F_SUBTYPE
-# define F_CRM_SYS_TO "crm_sys_to"
-# define F_CRM_SYS_FROM "crm_sys_from"
-# define F_CRM_HOST_FROM F_ORIG
-# define F_CRM_REFERENCE XML_ATTR_REFERENCE
-# define F_CRM_VERSION XML_ATTR_VERSION
-# define F_CRM_ORIGIN "origin"
-# define F_CRM_USER "crm_user"
-# define F_CRM_JOIN_ID "join_id"
-# define F_CRM_DC_LEAVING "dc-leaving"
-# define F_CRM_ELECTION_ID "election-id"
-# define F_CRM_ELECTION_AGE_S "election-age-sec"
-# define F_CRM_ELECTION_AGE_US "election-age-nano-sec"
-# define F_CRM_ELECTION_OWNER "election-owner"
-# define F_CRM_TGRAPH "crm-tgraph-file"
-# define F_CRM_TGRAPH_INPUT "crm-tgraph-in"
-
-# define F_CRM_THROTTLE_MODE "crm-limit-mode"
-# define F_CRM_THROTTLE_MAX "crm-limit-max"
-
-/*---- Common tags/attrs */
-# define XML_DIFF_MARKER "__crm_diff_marker__"
-# define XML_TAG_CIB "cib"
-# define XML_TAG_FAILED "failed"
-
-# define XML_ATTR_CRM_VERSION "crm_feature_set"
-# define XML_ATTR_DIGEST "digest"
-# define XML_ATTR_VALIDATION "validate-with"
-
-# define XML_ATTR_QUORUM_PANIC "no-quorum-panic"
-# define XML_ATTR_HAVE_QUORUM "have-quorum"
-# define XML_ATTR_HAVE_WATCHDOG "have-watchdog"
-# define XML_ATTR_GENERATION "epoch"
-# define XML_ATTR_GENERATION_ADMIN "admin_epoch"
-# define XML_ATTR_NUMUPDATES "num_updates"
-# define XML_ATTR_TIMEOUT "timeout"
-# define XML_ATTR_ORIGIN "crm-debug-origin"
-# define XML_ATTR_TSTAMP "crm-timestamp"
-# define XML_CIB_ATTR_WRITTEN "cib-last-written"
-# define XML_ATTR_VERSION "version"
-# define XML_ATTR_DESC "description"
-# define XML_ATTR_ID "id"
-# define XML_ATTR_NAME "name"
-# define XML_ATTR_IDREF "id-ref"
-# define XML_ATTR_ID_LONG "long-id"
-# define XML_ATTR_TYPE "type"
-# define XML_ATTR_OP "op"
-# define XML_ATTR_DC_UUID "dc-uuid"
-# define XML_ATTR_UPDATE_ORIG "update-origin"
-# define XML_ATTR_UPDATE_CLIENT "update-client"
-# define XML_ATTR_UPDATE_USER "update-user"
-
-# define XML_BOOLEAN_TRUE "true"
-# define XML_BOOLEAN_FALSE "false"
-# define XML_BOOLEAN_YES XML_BOOLEAN_TRUE
-# define XML_BOOLEAN_NO XML_BOOLEAN_FALSE
-
-# define XML_TAG_OPTIONS "options"
-
-/*---- top level tags/attrs */
-# define XML_ATTR_REQUEST "request"
-# define XML_ATTR_RESPONSE "response"
-
-# define XML_ATTR_UNAME "uname"
-# define XML_ATTR_REFERENCE "reference"
-
-# define XML_CRM_TAG_PING "ping_response"
-# define XML_PING_ATTR_STATUS "result"
-# define XML_PING_ATTR_SYSFROM "crm_subsystem"
-# define XML_PING_ATTR_CRMDSTATE "crmd_state"
-# define XML_PING_ATTR_PACEMAKERDSTATE "pacemakerd_state"
-# define XML_PING_ATTR_PACEMAKERDSTATE_INIT "init"
-# define XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS "starting_daemons"
-# define XML_PING_ATTR_PACEMAKERDSTATE_WAITPING "wait_for_ping"
-# define XML_PING_ATTR_PACEMAKERDSTATE_RUNNING "running"
-# define XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN "shutting_down"
-# define XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE "shutdown_complete"
-# define XML_PING_ATTR_PACEMAKERDSTATE_REMOTE "remote"
-
-# define XML_FAIL_TAG_CIB "failed_update"
-
-# define XML_FAILCIB_ATTR_ID "id"
-# define XML_FAILCIB_ATTR_OBJTYPE "object_type"
-# define XML_FAILCIB_ATTR_OP "operation"
-# define XML_FAILCIB_ATTR_REASON "reason"
-
-/*---- CIB specific tags/attrs */
-# define XML_CIB_TAG_SECTION_ALL "all"
-# define XML_CIB_TAG_CONFIGURATION "configuration"
-# define XML_CIB_TAG_STATUS "status"
-# define XML_CIB_TAG_RESOURCES "resources"
-# define XML_CIB_TAG_NODES "nodes"
-# define XML_CIB_TAG_CONSTRAINTS "constraints"
-# define XML_CIB_TAG_CRMCONFIG "crm_config"
-# define XML_CIB_TAG_OPCONFIG "op_defaults"
-# define XML_CIB_TAG_RSCCONFIG "rsc_defaults"
-# define XML_CIB_TAG_ACLS "acls"
-# define XML_CIB_TAG_ALERTS "alerts"
-# define XML_CIB_TAG_ALERT "alert"
-# define XML_CIB_TAG_ALERT_RECIPIENT "recipient"
-# define XML_CIB_TAG_ALERT_SELECT "select"
-# define XML_CIB_TAG_ALERT_ATTRIBUTES "select_attributes"
-# define XML_CIB_TAG_ALERT_FENCING "select_fencing"
-# define XML_CIB_TAG_ALERT_NODES "select_nodes"
-# define XML_CIB_TAG_ALERT_RESOURCES "select_resources"
-# define XML_CIB_TAG_ALERT_ATTR "attribute"
-
-# define XML_CIB_TAG_STATE "node_state"
-# define XML_CIB_TAG_NODE "node"
-# define XML_CIB_TAG_NVPAIR "nvpair"
-
-# define XML_CIB_TAG_PROPSET "cluster_property_set"
-# define XML_TAG_ATTR_SETS "instance_attributes"
-# define XML_TAG_META_SETS "meta_attributes"
-# define XML_TAG_ATTRS "attributes"
-# define XML_TAG_PARAMS "parameters"
-# define XML_TAG_PARAM "param"
-# define XML_TAG_UTILIZATION "utilization"
-
-# define XML_TAG_RESOURCE_REF "resource_ref"
-# define XML_CIB_TAG_RESOURCE "primitive"
-# define XML_CIB_TAG_GROUP "group"
-# define XML_CIB_TAG_INCARNATION "clone"
-# define XML_CIB_TAG_CONTAINER "bundle"
-
-# define XML_CIB_TAG_RSC_TEMPLATE "template"
-
-# define XML_RSC_ATTR_TARGET "container-attribute-target"
-# define XML_RSC_ATTR_RESTART "restart-type"
-# define XML_RSC_ATTR_ORDERED "ordered"
-# define XML_RSC_ATTR_INTERLEAVE "interleave"
-# define XML_RSC_ATTR_INCARNATION "clone"
-# define XML_RSC_ATTR_PROMOTABLE "promotable"
-# define XML_RSC_ATTR_MANAGED "is-managed"
-# define XML_RSC_ATTR_TARGET_ROLE "target-role"
-# define XML_RSC_ATTR_UNIQUE "globally-unique"
-# define XML_RSC_ATTR_NOTIFY "notify"
-# define XML_RSC_ATTR_STICKINESS "resource-stickiness"
-# define XML_RSC_ATTR_MULTIPLE "multiple-active"
-# define XML_RSC_ATTR_REQUIRES "requires"
-# define XML_RSC_ATTR_CONTAINER "container"
-# define XML_RSC_ATTR_INTERNAL_RSC "internal_rsc"
-# define XML_RSC_ATTR_MAINTENANCE "maintenance"
-# define XML_RSC_ATTR_REMOTE_NODE "remote-node"
-# define XML_RSC_ATTR_CLEAR_OP "clear_failure_op"
-# define XML_RSC_ATTR_CLEAR_INTERVAL "clear_failure_interval"
-# define XML_RSC_ATTR_REMOTE_RA_ADDR "addr"
-# define XML_RSC_ATTR_REMOTE_RA_SERVER "server"
-# define XML_RSC_ATTR_REMOTE_RA_PORT "port"
-# define XML_RSC_ATTR_CRITICAL "critical"
-
-# define XML_REMOTE_ATTR_RECONNECT_INTERVAL "reconnect_interval"
-
-# define XML_OP_ATTR_ON_FAIL "on-fail"
-# define XML_OP_ATTR_START_DELAY "start-delay"
-# define XML_OP_ATTR_ALLOW_MIGRATE "allow-migrate"
-# define XML_OP_ATTR_ORIGIN "interval-origin"
-# define XML_OP_ATTR_PENDING "record-pending"
-# define XML_OP_ATTR_DIGESTS_ALL "digests-all"
-# define XML_OP_ATTR_DIGESTS_SECURE "digests-secure"
-
-# define XML_CIB_TAG_LRM "lrm"
-# define XML_LRM_TAG_RESOURCES "lrm_resources"
-# define XML_LRM_TAG_RESOURCE "lrm_resource"
-# define XML_LRM_TAG_RSC_OP "lrm_rsc_op"
-# define XML_AGENT_ATTR_CLASS "class"
-# define XML_AGENT_ATTR_PROVIDER "provider"
-
-//! \deprecated Do not use (will be removed in a future release)
-# define XML_CIB_ATTR_REPLACE "replace"
-
-# define XML_CIB_ATTR_PRIORITY "priority"
-
-# define XML_NODE_IS_REMOTE "remote_node"
-# define XML_NODE_IS_FENCED "node_fenced"
-# define XML_NODE_IS_MAINTENANCE "node_in_maintenance"
-
-# define XML_CIB_ATTR_SHUTDOWN "shutdown"
-
-/* Aside from being an old name for the executor, LRM is a misnomer here because
- * the controller and scheduler use these to track actions, which are not always
- * executor operations.
- */
-
-// XML attribute that takes interval specification (user-facing configuration)
-# define XML_LRM_ATTR_INTERVAL "interval"
-
-// XML attribute that takes interval in milliseconds (daemon APIs)
-// (identical value as above, but different constant allows clearer code intent)
-# define XML_LRM_ATTR_INTERVAL_MS XML_LRM_ATTR_INTERVAL
-
-# define XML_LRM_ATTR_TASK "operation"
-# define XML_LRM_ATTR_TASK_KEY "operation_key"
-# define XML_LRM_ATTR_TARGET "on_node"
-# define XML_LRM_ATTR_TARGET_UUID "on_node_uuid"
-/*! Actions to be executed on Pacemaker Remote nodes are routed through the
- * controller on the cluster node hosting the remote connection. That cluster
- * node is considered the router node for the action.
- */
-# define XML_LRM_ATTR_ROUTER_NODE "router_node"
-# define XML_LRM_ATTR_RSCID "rsc-id"
-# define XML_LRM_ATTR_OPSTATUS "op-status"
-# define XML_LRM_ATTR_RC "rc-code"
-# define XML_LRM_ATTR_CALLID "call-id"
-# define XML_LRM_ATTR_OP_DIGEST "op-digest"
-# define XML_LRM_ATTR_OP_RESTART "op-force-restart"
-# define XML_LRM_ATTR_OP_SECURE "op-secure-params"
-# define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest"
-# define XML_LRM_ATTR_SECURE_DIGEST "op-secure-digest"
-# define XML_LRM_ATTR_EXIT_REASON "exit-reason"
-
-# define XML_RSC_OP_LAST_CHANGE "last-rc-change"
-# define XML_RSC_OP_T_EXEC "exec-time"
-# define XML_RSC_OP_T_QUEUE "queue-time"
-
-# define XML_LRM_ATTR_MIGRATE_SOURCE "migrate_source"
-# define XML_LRM_ATTR_MIGRATE_TARGET "migrate_target"
-
-# define XML_TAG_GRAPH "transition_graph"
-# define XML_GRAPH_TAG_RSC_OP "rsc_op"
-# define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event"
-# define XML_GRAPH_TAG_CRM_EVENT "crm_event"
-# define XML_GRAPH_TAG_DOWNED "downed"
-# define XML_GRAPH_TAG_MAINTENANCE "maintenance"
-
-# define XML_TAG_RULE "rule"
-# define XML_RULE_ATTR_SCORE "score"
-# define XML_RULE_ATTR_SCORE_ATTRIBUTE "score-attribute"
-# define XML_RULE_ATTR_ROLE "role"
-# define XML_RULE_ATTR_BOOLEAN_OP "boolean-op"
-
-# define XML_TAG_EXPRESSION "expression"
-# define XML_EXPR_ATTR_ATTRIBUTE "attribute"
-# define XML_EXPR_ATTR_OPERATION "operation"
-# define XML_EXPR_ATTR_VALUE "value"
-# define XML_EXPR_ATTR_TYPE "type"
-# define XML_EXPR_ATTR_VALUE_SOURCE "value-source"
-
-# define XML_CONS_TAG_RSC_DEPEND "rsc_colocation"
-# define XML_CONS_TAG_RSC_ORDER "rsc_order"
-# define XML_CONS_TAG_RSC_LOCATION "rsc_location"
-# define XML_CONS_TAG_RSC_TICKET "rsc_ticket"
-# define XML_CONS_TAG_RSC_SET "resource_set"
-# define XML_CONS_ATTR_SYMMETRICAL "symmetrical"
-
-# define XML_LOCATION_ATTR_DISCOVERY "resource-discovery"
-
-# define XML_COLOC_ATTR_SOURCE "rsc"
-# define XML_COLOC_ATTR_SOURCE_ROLE "rsc-role"
-# define XML_COLOC_ATTR_TARGET "with-rsc"
-# define XML_COLOC_ATTR_TARGET_ROLE "with-rsc-role"
-# define XML_COLOC_ATTR_NODE_ATTR "node-attribute"
-# define XML_COLOC_ATTR_INFLUENCE "influence"
-
-//! \deprecated Deprecated since 2.1.5
-# define XML_COLOC_ATTR_SOURCE_INSTANCE "rsc-instance"
-
-//! \deprecated Deprecated since 2.1.5
-# define XML_COLOC_ATTR_TARGET_INSTANCE "with-rsc-instance"
-
-# define XML_LOC_ATTR_SOURCE "rsc"
-# define XML_LOC_ATTR_SOURCE_PATTERN "rsc-pattern"
-
-# define XML_ORDER_ATTR_FIRST "first"
-# define XML_ORDER_ATTR_THEN "then"
-# define XML_ORDER_ATTR_FIRST_ACTION "first-action"
-# define XML_ORDER_ATTR_THEN_ACTION "then-action"
-# define XML_ORDER_ATTR_KIND "kind"
-
-//! \deprecated Deprecated since 2.1.5
-# define XML_ORDER_ATTR_FIRST_INSTANCE "first-instance"
-
-//! \deprecated Deprecated since 2.1.5
-# define XML_ORDER_ATTR_THEN_INSTANCE "then-instance"
-
-# define XML_TICKET_ATTR_TICKET "ticket"
-# define XML_TICKET_ATTR_LOSS_POLICY "loss-policy"
-
-# define XML_NVPAIR_ATTR_NAME "name"
-# define XML_NVPAIR_ATTR_VALUE "value"
-
-# define XML_NODE_ATTR_RSC_DISCOVERY "resource-discovery-enabled"
-
-# define XML_CONFIG_ATTR_DC_DEADTIME "dc-deadtime"
-# define XML_CONFIG_ATTR_ELECTION_FAIL "election-timeout"
-# define XML_CONFIG_ATTR_FORCE_QUIT "shutdown-escalation"
-# define XML_CONFIG_ATTR_RECHECK "cluster-recheck-interval"
-# define XML_CONFIG_ATTR_FENCE_REACTION "fence-reaction"
-# define XML_CONFIG_ATTR_SHUTDOWN_LOCK "shutdown-lock"
-# define XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT "shutdown-lock-limit"
-# define XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY "priority-fencing-delay"
-# define XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT "node-pending-timeout"
-
-# define XML_ALERT_ATTR_PATH "path"
-# define XML_ALERT_ATTR_TIMEOUT "timeout"
-# define XML_ALERT_ATTR_TSTAMP_FORMAT "timestamp-format"
-# define XML_ALERT_ATTR_REC_VALUE "value"
-
-# define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple"
-
-# define XML_ATTR_TRANSITION_MAGIC "transition-magic"
-# define XML_ATTR_TRANSITION_KEY "transition-key"
-
-# define XML_ATTR_TE_NOWAIT "op_no_wait"
-# define XML_ATTR_TE_TARGET_RC "op_target_rc"
-# define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes"
-
-//! \deprecated Do not use (will be removed in a future release)
-# define XML_TAG_DIFF_ADDED "diff-added"
-
-//! \deprecated Do not use (will be removed in a future release)
-# define XML_TAG_DIFF_REMOVED "diff-removed"
-
-# define XML_ACL_TAG_USER "acl_target"
-# define XML_ACL_TAG_USERv1 "acl_user"
-# define XML_ACL_TAG_GROUP "acl_group"
-# define XML_ACL_TAG_ROLE "acl_role"
-# define XML_ACL_TAG_PERMISSION "acl_permission"
-# define XML_ACL_TAG_ROLE_REF "role"
-# define XML_ACL_TAG_ROLE_REFv1 "role_ref"
-# define XML_ACL_ATTR_KIND "kind"
-# define XML_ACL_TAG_READ "read"
-# define XML_ACL_TAG_WRITE "write"
-# define XML_ACL_TAG_DENY "deny"
-# define XML_ACL_ATTR_REF "reference"
-# define XML_ACL_ATTR_REFv1 "ref"
-# define XML_ACL_ATTR_TAG "object-type"
-# define XML_ACL_ATTR_TAGv1 "tag"
-# define XML_ACL_ATTR_XPATH "xpath"
-# define XML_ACL_ATTR_ATTRIBUTE "attribute"
-
-# define XML_CIB_TAG_TICKETS "tickets"
-# define XML_CIB_TAG_TICKET_STATE "ticket_state"
-
-# define XML_CIB_TAG_TAGS "tags"
-# define XML_CIB_TAG_TAG "tag"
-# define XML_CIB_TAG_OBJ_REF "obj_ref"
-
-# define XML_TAG_FENCING_TOPOLOGY "fencing-topology"
-# define XML_TAG_FENCING_LEVEL "fencing-level"
-# define XML_ATTR_STONITH_INDEX "index"
-# define XML_ATTR_STONITH_TARGET "target"
-# define XML_ATTR_STONITH_TARGET_VALUE "target-value"
-# define XML_ATTR_STONITH_TARGET_PATTERN "target-pattern"
-# define XML_ATTR_STONITH_TARGET_ATTRIBUTE "target-attribute"
-# define XML_ATTR_STONITH_DEVICES "devices"
-
-# define XML_TAG_DIFF "diff"
-# define XML_DIFF_VERSION "version"
-# define XML_DIFF_VSOURCE "source"
-# define XML_DIFF_VTARGET "target"
-# define XML_DIFF_CHANGE "change"
-# define XML_DIFF_LIST "change-list"
-# define XML_DIFF_ATTR "change-attr"
-# define XML_DIFF_RESULT "change-result"
-# define XML_DIFF_OP "operation"
-# define XML_DIFF_PATH "path"
-# define XML_DIFF_POSITION "position"
-
-# define ID(x) crm_element_value(x, XML_ATTR_ID)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+#endif // PCMK__CRM_MSG_XML__H
diff --git a/include/crm/msg_xml_compat.h b/include/crm/msg_xml_compat.h
index 612eebf..e4170b3 100644
--- a/include/crm/msg_xml_compat.h
+++ b/include/crm/msg_xml_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -11,6 +11,7 @@
# define PCMK__CRM_MSG_XML_COMPAT__H
#include <crm/common/agents.h> // PCMK_STONITH_PROVIDES
+#include <crm/common/xml.h>
#ifdef __cplusplus
extern "C" {
@@ -43,27 +44,36 @@ extern "C" {
//! \deprecated Use PCMK_STONITH_PROVIDES instead
#define XML_RSC_ATTR_PROVIDES PCMK_STONITH_PROVIDES
-//! \deprecated Use PCMK_XE_PROMOTABLE_LEGACY instead
+//! \deprecated Do not use
+#define PCMK_XE_PROMOTABLE_LEGACY "master"
+
+//! \deprecated Do not use
#define XML_CIB_TAG_MASTER PCMK_XE_PROMOTABLE_LEGACY
-//! \deprecated Use PCMK_XA_PROMOTED_MAX_LEGACY instead
+//! \deprecated Do not use
+#define PCMK_XA_PROMOTED_MAX_LEGACY "master-max"
+
+//! \deprecated Do not use
#define PCMK_XE_PROMOTED_MAX_LEGACY PCMK_XA_PROMOTED_MAX_LEGACY
-//! \deprecated Use PCMK_XA_PROMOTED_MAX_LEGACY instead
+//! \deprecated Do not use
#define XML_RSC_ATTR_MASTER_MAX PCMK_XA_PROMOTED_MAX_LEGACY
-//! \deprecated Use PCMK_XA_PROMOTED_NODE_MAX_LEGACY instead
+//! \deprecated Do not use
+#define PCMK_XA_PROMOTED_NODE_MAX_LEGACY "master-node-max"
+
+//! \deprecated Do not use
#define PCMK_XE_PROMOTED_NODE_MAX_LEGACY PCMK_XA_PROMOTED_NODE_MAX_LEGACY
+//! \deprecated Do not use
+#define XML_RSC_ATTR_MASTER_NODEMAX PCMK_XA_PROMOTED_NODE_MAX_LEGACY
+
//! \deprecated Use PCMK_META_MIGRATION_THRESHOLD instead
#define XML_RSC_ATTR_FAIL_STICKINESS PCMK_META_MIGRATION_THRESHOLD
//! \deprecated Use PCMK_META_FAILURE_TIMEOUT instead
#define XML_RSC_ATTR_FAIL_TIMEOUT PCMK_META_FAILURE_TIMEOUT
-//! \deprecated Use PCMK_XA_PROMOTED_NODE_MAX_LEGACY instead
-#define XML_RSC_ATTR_MASTER_NODEMAX PCMK_XA_PROMOTED_NODE_MAX_LEGACY
-
//! \deprecated Do not use (will be removed in a future release)
#define XML_ATTR_RA_VERSION "ra-version"
@@ -79,7 +89,7 @@ extern "C" {
//! \deprecated Do not use (will be removed in a future release)
#define XML_TAG_OP_VER_META "op_versioned_meta"
-//! \deprecated Use \p XML_ATTR_ID instead
+//! \deprecated Use \p PCMK_XA_ID instead
#define XML_ATTR_UUID "id"
//! \deprecated Do not use (will be removed in a future release)
@@ -109,6 +119,902 @@ extern "C" {
//! \deprecated Use name member directly
#define TYPE(x) (((x) == NULL)? NULL : (const char *) ((x)->name))
+//! \deprecated Use \c PCMK_OPT_CLUSTER_RECHECK_INTERVAL instead
+#define XML_CONFIG_ATTR_RECHECK PCMK_OPT_CLUSTER_RECHECK_INTERVAL
+
+//! \deprecated Use \c PCMK_OPT_DC_DEADTIME instead
+#define XML_CONFIG_ATTR_DC_DEADTIME PCMK_OPT_DC_DEADTIME
+
+//! \deprecated Use \c PCMK_OPT_ELECTION_TIMEOUT instead
+#define XML_CONFIG_ATTR_ELECTION_FAIL PCMK_OPT_ELECTION_TIMEOUT
+
+//! \deprecated Use \c PCMK_OPT_FENCE_REACTION instead
+#define XML_CONFIG_ATTR_FENCE_REACTION PCMK_OPT_FENCE_REACTION
+
+//! \deprecated Use \c PCMK_OPT_HAVE_WATCHDOG instead
+#define XML_ATTR_HAVE_WATCHDOG PCMK_OPT_HAVE_WATCHDOG
+
+//! \deprecated Use \c PCMK_OPT_NODE_PENDING_TIMEOUT instead
+#define XML_CONFIG_ATTR_NODE_PENDING_TIMEOUT PCMK_OPT_NODE_PENDING_TIMEOUT
+
+//! \deprecated Use \c PCMK_OPT_PRIORITY_FENCING_DELAY instead
+#define XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY PCMK_OPT_PRIORITY_FENCING_DELAY
+
+//! \deprecated Use \c PCMK_OPT_SHUTDOWN_ESCALATION instead
+#define XML_CONFIG_ATTR_FORCE_QUIT PCMK_OPT_SHUTDOWN_ESCALATION
+
+//! \deprecated Use \c PCMK_OPT_SHUTDOWN_LOCK instead
+#define XML_CONFIG_ATTR_SHUTDOWN_LOCK PCMK_OPT_SHUTDOWN_LOCK
+
+//! \deprecated Use \c PCMK_OPT_SHUTDOWN_LOCK_LIMIT instead
+#define XML_CONFIG_ATTR_SHUTDOWN_LOCK_LIMIT PCMK_OPT_SHUTDOWN_LOCK_LIMIT
+
+//! \deprecated Use \c PCMK_XA_CRM_FEATURE_SET instead
+#define XML_ATTR_CRM_VERSION PCMK_XA_CRM_FEATURE_SET
+
+//! \deprecated Do not use
+#define XML_ATTR_DIGEST "digest"
+
+//! \deprecated Use \c PCMK_XA_VALIDATE_WITH instead
+#define XML_ATTR_VALIDATION PCMK_XA_VALIDATE_WITH
+
+//! \deprecated Use \c PCMK_XA_NO_QUORUM_PANIC instead
+#define XML_ATTR_QUORUM_PANIC PCMK_XA_NO_QUORUM_PANIC
+
+//! \deprecated Use \c PCMK_XA_HAVE_QUORUM instead
+#define XML_ATTR_HAVE_QUORUM PCMK_XA_HAVE_QUORUM
+
+//! \deprecated Use \c PCMK_XA_EPOCH instead
+#define XML_ATTR_GENERATION PCMK_XA_EPOCH
+
+//! \deprecated Use \c PCMK_XA_ADMIN_EPOCH instead
+#define XML_ATTR_GENERATION_ADMIN PCMK_XA_ADMIN_EPOCH
+
+//! \deprecated Use \c PCMK_XA_NUM_UPDATES instead
+#define XML_ATTR_NUMUPDATES PCMK_XA_NUM_UPDATES
+
+//! \deprecated Use \c PCMK_XA_CRM_DEBUG_ORIGIN instead
+#define XML_ATTR_ORIGIN PCMK_XA_CRM_DEBUG_ORIGIN
+
+//! \deprecated Use \c PCMK_XA_CRM_TIMESTAMP instead
+#define XML_ATTR_TSTAMP PCMK_XA_CRM_TIMESTAMP
+
+//! \deprecated Use \c PCMK_XA_CIB_LAST_WRITTEN instead
+#define XML_CIB_ATTR_WRITTEN PCMK_XA_CIB_LAST_WRITTEN
+
+//! \deprecated Use \c PCMK_XA_VERSION instead
+#define XML_ATTR_VERSION PCMK_XA_VERSION
+
+//! \deprecated Use \c PCMK_XA_DESCRIPTION instead
+#define XML_ATTR_DESC PCMK_XA_DESCRIPTION
+
+//! \deprecated Use \c PCMK_XA_ID instead
+#define XML_ATTR_ID PCMK_XA_ID
+
+//! \deprecated Use \c PCMK_XA_ID instead
+#define XML_FAILCIB_ATTR_ID PCMK_XA_ID
+
+//! \deprecated Use \c PCMK_META_CONTAINER_ATTRIBUTE_TARGET instead
+#define XML_RSC_ATTR_TARGET PCMK_META_CONTAINER_ATTRIBUTE_TARGET
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_RESTART "restart-type"
+
+//! \deprecated Use \c PCMK_META_ORDERED instead
+#define XML_RSC_ATTR_ORDERED PCMK_META_ORDERED
+
+//! \deprecated Use \c PCMK_META_INTERLEAVE instead
+#define XML_RSC_ATTR_INTERLEAVE PCMK_META_INTERLEAVE
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_INCARNATION "clone"
+
+//! \deprecated Use \c PCMK_META_PROMOTABLE instead
+#define XML_RSC_ATTR_PROMOTABLE PCMK_META_PROMOTABLE
+
+//! \deprecated Use \c PCMK_META_IS_MANAGED instead
+#define XML_RSC_ATTR_MANAGED PCMK_META_IS_MANAGED
+
+//! \deprecated Use \c PCMK_META_TARGET_ROLE instead
+#define XML_RSC_ATTR_TARGET_ROLE PCMK_META_TARGET_ROLE
+
+//! \deprecated Use \c PCMK_META_GLOBALLY_UNIQUE instead
+#define XML_RSC_ATTR_UNIQUE PCMK_META_GLOBALLY_UNIQUE
+
+//! \deprecated Use \c PCMK_META_NOTIFY instead
+#define XML_RSC_ATTR_NOTIFY PCMK_META_NOTIFY
+
+//! \deprecated Use \c PCMK_META_RESOURCE_STICKINESS instead
+#define XML_RSC_ATTR_STICKINESS PCMK_META_RESOURCE_STICKINESS
+
+//! \deprecated Use \c PCMK_META_MULTIPLE_ACTIVE instead
+#define XML_RSC_ATTR_MULTIPLE PCMK_META_MULTIPLE_ACTIVE
+
+//! \deprecated Use \c PCMK_META_REQUIRES instead
+#define XML_RSC_ATTR_REQUIRES PCMK_META_REQUIRES
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_CONTAINER "container"
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_INTERNAL_RSC "internal_rsc"
+
+//! \deprecated Use \c PCMK_META_MAINTENANCE instead
+#define XML_RSC_ATTR_MAINTENANCE PCMK_META_MAINTENANCE
+
+//! \deprecated Use \c PCMK_META_REMOTE_NODE instead
+#define XML_RSC_ATTR_REMOTE_NODE PCMK_META_REMOTE_NODE
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_CLEAR_OP "clear_failure_op"
+
+//! \deprecated Do not use
+#define XML_RSC_ATTR_CLEAR_INTERVAL "clear_failure_interval"
+
+//! \deprecated Use \c PCMK_META_CRITICAL instead
+#define XML_RSC_ATTR_CRITICAL PCMK_META_CRITICAL
+
+//! \deprecated Use \c PCMK_META_ALLOW_MIGRATE instead
+#define XML_OP_ATTR_ALLOW_MIGRATE PCMK_META_ALLOW_MIGRATE
+
+//! \deprecated Use \c PCMK_VALUE_TRUE instead
+#define XML_BOOLEAN_YES PCMK_VALUE_TRUE
+
+//! \deprecated Use \c PCMK_VALUE_FALSE instead
+#define XML_BOOLEAN_NO PCMK_VALUE_FALSE
+
+//! \deprecated Use \c PCMK_REMOTE_RA_ADDR instead
+#define XML_RSC_ATTR_REMOTE_RA_ADDR PCMK_REMOTE_RA_ADDR
+
+//! \deprecated Use \c PCMK_REMOTE_RA_SERVER instead
+#define XML_RSC_ATTR_REMOTE_RA_SERVER PCMK_REMOTE_RA_SERVER
+
+//! \deprecated Use \c PCMK_REMOTE_RA_PORT instead
+#define XML_RSC_ATTR_REMOTE_RA_PORT PCMK_REMOTE_RA_PORT
+
+//! \deprecated Use \c PCMK_REMOTE_RA_RECONNECT_INTERVAL instead
+#define XML_REMOTE_ATTR_RECONNECT_INTERVAL PCMK_REMOTE_RA_RECONNECT_INTERVAL
+
+//! \deprecated Use \c PCMK_XA_NAME instead
+#define XML_ATTR_NAME PCMK_XA_NAME
+
+//! \deprecated Use \c PCMK_XA_NAME instead
+#define XML_NVPAIR_ATTR_NAME PCMK_XA_NAME
+
+//! \deprecated Use \c PCMK_XA_VALUE instead
+#define XML_EXPR_ATTR_VALUE PCMK_XA_VALUE
+
+//! \deprecated Use \c PCMK_XA_VALUE instead
+#define XML_NVPAIR_ATTR_VALUE PCMK_XA_VALUE
+
+//! \deprecated Use \c PCMK_XA_VALUE instead
+#define XML_ALERT_ATTR_REC_VALUE PCMK_XA_VALUE
+
+//! \deprecated Use \c PCMK_XA_ID_REF instead
+#define XML_ATTR_IDREF PCMK_XA_ID_REF
+
+//! \deprecated Do not use
+#define XML_ATTR_ID_LONG "long-id"
+
+//! \deprecated Use \c PCMK_XA_TYPE instead
+#define XML_ATTR_TYPE PCMK_XA_TYPE
+
+//! \deprecated Use \c PCMK_XA_TYPE instead
+#define XML_EXPR_ATTR_TYPE PCMK_XA_TYPE
+
+//! \deprecated Use \c PCMK_XA_PROVIDER instead
+#define XML_AGENT_ATTR_PROVIDER PCMK_XA_PROVIDER
+
+//! \deprecated Use \c PCMK_XA_CLASS instead
+#define XML_AGENT_ATTR_CLASS PCMK_XA_CLASS
+
+//! \deprecated Use \c PCMK_XE_OP instead
+#define XML_ATTR_OP PCMK_XE_OP
+
+//! \deprecated Use \c PCMK_XA_DC_UUID instead
+#define XML_ATTR_DC_UUID PCMK_XA_DC_UUID
+
+//! \deprecated Use \c PCMK_XA_UPDATE_ORIGIN instead
+#define XML_ATTR_UPDATE_ORIG PCMK_XA_UPDATE_ORIGIN
+
+//! \deprecated Use \c PCMK_XA_UPDATE_CLIENT instead
+#define XML_ATTR_UPDATE_CLIENT PCMK_XA_UPDATE_CLIENT
+
+//! \deprecated Use \c PCMK_XA_UPDATE_USER instead
+#define XML_ATTR_UPDATE_USER PCMK_XA_UPDATE_USER
+
+//! \deprecated Use \c PCMK_XA_REQUEST instead
+#define XML_ATTR_REQUEST PCMK_XA_REQUEST
+
+//! \deprecated Do not use
+#define XML_ATTR_RESPONSE "response"
+
+//! \deprecated Use \c PCMK_XA_UNAME instead
+#define XML_ATTR_UNAME PCMK_XA_UNAME
+
+//! \deprecated Use \c PCMK_XA_REFERENCE instead
+#define XML_ATTR_REFERENCE PCMK_XA_REFERENCE
+
+//! \deprecated Use \c PCMK_XA_REFERENCE instead
+#define XML_ACL_ATTR_REF PCMK_XA_REFERENCE
+
+//! \deprecated Use \c PCMK_XA_REFERENCE instead
+#define F_CRM_REFERENCE PCMK_XA_REFERENCE
+
+//! \deprecated Do not use
+#define XML_ATTR_TRANSITION_MAGIC "transition-magic"
+
+//! \deprecated Do not use
+#define XML_ATTR_TRANSITION_KEY "transition-key"
+
+//! \deprecated Use \c PCMK_XA_INDEX instead
+#define XML_ATTR_STONITH_INDEX PCMK_XA_INDEX
+
+//! \deprecated Use \c PCMK_XA_TARGET instead
+#define XML_ATTR_STONITH_TARGET PCMK_XA_TARGET
+
+//! \deprecated Use \c PCMK_XA_TARGET_VALUE instead
+#define XML_ATTR_STONITH_TARGET_VALUE PCMK_XA_TARGET_VALUE
+
+//! \deprecated Use \c PCMK_XA_TARGET_PATTERN instead
+#define XML_ATTR_STONITH_TARGET_PATTERN PCMK_XA_TARGET_PATTERN
+
+//! \deprecated Use \c PCMK_XA_TARGET_ATTRIBUTE instead
+#define XML_ATTR_STONITH_TARGET_ATTRIBUTE PCMK_XA_TARGET_ATTRIBUTE
+
+//! \deprecated Use \c PCMK_XA_DEVICES instead
+#define XML_ATTR_STONITH_DEVICES PCMK_XA_DEVICES
+
+#ifndef F_ORIG
+//! \deprecated Do not use
+#define F_ORIG "src"
+#endif
+
+//! \deprecated Do not use
+#define F_CRM_HOST_FROM F_ORIG
+
+#ifndef F_SEQ
+//! \deprecated Do not use
+#define F_SEQ "seq"
+#endif
+
+#ifndef F_SUBTYPE
+//! \deprecated Do not use
+#define F_SUBTYPE "subt"
+#endif
+
+//! \deprecated Do not use
+#define F_CRM_MSG_TYPE F_SUBTYPE
+
+#ifndef F_TYPE
+//! \deprecated Do not use
+#define F_TYPE "t"
+#endif
+
+#ifndef F_CLIENTNAME
+//! \deprecated Do not use
+#define F_CLIENTNAME "cn"
+#endif
+
+#ifndef F_XML_TAGNAME
+//! \deprecated Do not use
+#define F_XML_TAGNAME "__name__"
+#endif
+
+//! \deprecated Use \c PCMK_VALUE_TRUE instead
+#define XML_BOOLEAN_TRUE PCMK_VALUE_TRUE
+
+//! \deprecated Use \c PCMK_VALUE_FALSE instead
+#define XML_BOOLEAN_FALSE PCMK_VALUE_FALSE
+
+//! \deprecated Do not use
+#define F_CRM_TASK "crm_task"
+
+//! \deprecated Do not use
+#define F_CRM_HOST_TO "crm_host_to"
+
+//! \deprecated Do not use
+#define F_CRM_SYS_TO "crm_sys_to"
+
+//! \deprecated Do not use
+#define F_CRM_SYS_FROM "crm_sys_from"
+
+//! \deprecated Use \c PCMK_XA_VERSION instead
+#define F_CRM_VERSION PCMK_XA_VERSION
+
+//! \deprecated Use \c PCMK_XA_ORIGIN instead
+#define F_CRM_ORIGIN PCMK_XA_ORIGIN
+
+//! \deprecated Do not use
+#define F_CRM_USER "crm_user"
+
+//! \deprecated Do not use
+#define F_CRM_JOIN_ID "join_id"
+
+//! \deprecated Do not use
+#define F_CRM_DC_LEAVING "dc-leaving"
+
+//! \deprecated Do not use
+#define F_CRM_ELECTION_ID "election-id"
+
+//! \deprecated Do not use
+#define F_CRM_ELECTION_AGE_S "election-age-sec"
+
+//! \deprecated Do not use
+#define F_CRM_ELECTION_AGE_US "election-age-nano-sec"
+
+//! \deprecated Do not use
+#define F_CRM_ELECTION_OWNER "election-owner"
+
+//! \deprecated Do not use
+#define F_CRM_TGRAPH "crm-tgraph-file"
+
+//! \deprecated Do not use
+#define F_CRM_TGRAPH_INPUT "crm-tgraph-in"
+
+//! \deprecated Do not use
+#define F_CRM_THROTTLE_MODE "crm-limit-mode"
+
+//! \deprecated Do not use
+#define F_CRM_THROTTLE_MAX "crm-limit-max"
+
+//! \deprecated Use \c PCMK_XA_RESULT instead
+#define XML_PING_ATTR_STATUS PCMK_XA_RESULT
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_SYSFROM "crm_subsystem"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_CRMDSTATE "crmd_state"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE "pacemakerd_state"
+
+//! \deprecated Do not use
+#define XML_FAILCIB_ATTR_OBJTYPE "object_type"
+
+//! \deprecated Use \c PCMK_XA_OPERATION instead
+#define XML_FAILCIB_ATTR_OP PCMK_XA_OPERATION
+
+//! \deprecated Use \c PCMK_XA_OPERATION instead
+#define XML_LRM_ATTR_TASK PCMK_XA_OPERATION
+
+//! \deprecated Use \c PCMK_XA_OPERATION instead
+#define XML_EXPR_ATTR_OPERATION PCMK_XA_OPERATION
+
+//! \deprecated Use \c PCMK_XA_OPERATION instead
+#define XML_DIFF_OP PCMK_XA_OPERATION
+
+//! \deprecated Use \c PCMK_XA_REASON instead
+#define XML_FAILCIB_ATTR_REASON PCMK_XA_REASON
+
+//! \deprecated Use \c PCMK_META_TIMEOUT instead
+#define XML_ATTR_TIMEOUT PCMK_META_TIMEOUT
+
+//! \deprecated Use \c PCMK_META_TIMEOUT instead
+#define XML_ALERT_ATTR_TIMEOUT PCMK_META_TIMEOUT
+
+//! \deprecated Use \c PCMK_XA_PATH instead
+#define XML_ALERT_ATTR_PATH PCMK_XA_PATH
+
+//! \deprecated Use \c PCMK_XA_PATH instead
+#define XML_DIFF_PATH PCMK_XA_PATH
+
+//! \deprecated Use \c PCMK_META_TIMESTAMP_FORMAT instead
+#define XML_ALERT_ATTR_TSTAMP_FORMAT PCMK_META_TIMESTAMP_FORMAT
+
+//! \deprecated Use \c PCMK_META_INTERVAL instead
+#define XML_LRM_ATTR_INTERVAL PCMK_META_INTERVAL
+
+//! \deprecated Use \c PCMK_META_INTERVAL instead
+#define XML_LRM_ATTR_INTERVAL_MS PCMK_META_INTERVAL
+
+//! \deprecated Do not use
+#define XML_CIB_ATTR_REPLACE "replace"
+
+//! \deprecated Do not use
+#define XML_COLOC_ATTR_SOURCE_INSTANCE "rsc-instance"
+
+//! \deprecated Do not use
+#define XML_COLOC_ATTR_TARGET_INSTANCE "with-rsc-instance"
+
+//! \deprecated Use \c PCMK_META_ON_FAIL instead
+#define XML_OP_ATTR_ON_FAIL PCMK_META_ON_FAIL
+
+//! \deprecated Use \c PCMK_META_START_DELAY instead
+#define XML_OP_ATTR_START_DELAY PCMK_META_START_DELAY
+
+//! \deprecated Use \c PCMK_META_INTERVAL_ORIGIN instead
+#define XML_OP_ATTR_ORIGIN PCMK_META_INTERVAL_ORIGIN
+
+//! \deprecated Use \c PCMK_META_RECORD_PENDING instead
+#define XML_OP_ATTR_PENDING PCMK_META_RECORD_PENDING
+
+//! \deprecated Do not use
+#define XML_OP_ATTR_DIGESTS_ALL "digests-all"
+
+//! \deprecated Do not use
+#define XML_OP_ATTR_DIGESTS_SECURE "digests-secure"
+
+//! \deprecated Do not use
+#define XML_CIB_ATTR_PRIORITY "priority"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_TASK_KEY "operation_key"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_TARGET "on_node"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_TARGET_UUID "on_node_uuid"
+
+//! \deprecated Do not use
+#define XML_ORDER_ATTR_FIRST_INSTANCE "first-instance"
+
+//! \deprecated Do not use
+#define XML_ORDER_ATTR_THEN_INSTANCE "then-instance"
+
+//! \deprecated Do not use
+#define XML_TAG_DIFF_ADDED "diff-added"
+
+//! \deprecated Do not use
+#define XML_TAG_DIFF_REMOVED "diff-removed"
+
+//! \deprecated Do not use
+#define XML_ATTR_TE_NOWAIT "op_no_wait"
+
+//! \deprecated Do not use
+#define XML_ATTR_TE_TARGET_RC "op_target_rc"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_ROUTER_NODE "router_node"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_RSCID "rsc-id"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_OPSTATUS "op-status"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_RC "rc-code"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_CALLID "call-id"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_OP_DIGEST "op-digest"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_OP_RESTART "op-force-restart"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_OP_SECURE "op-secure-params"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_SECURE_DIGEST "op-secure-digest"
+
+//! \deprecated Use \c PCMK_XA_EXIT_REASON instead
+#define XML_LRM_ATTR_EXIT_REASON PCMK_XA_EXIT_REASON
+
+//! \deprecated Use \c PCMK_XA_LAST_RC_CHANGE instead
+#define XML_RSC_OP_LAST_CHANGE PCMK_XA_LAST_RC_CHANGE
+
+//! \deprecated Use \c PCMK_XA_EXEC_TIME instead
+#define XML_RSC_OP_T_EXEC PCMK_XA_EXEC_TIME
+
+//! \deprecated Use \c PCMK_XA_QUEUE_TIME instead
+#define XML_RSC_OP_T_QUEUE PCMK_XA_QUEUE_TIME
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_MIGRATE_SOURCE "migrate_source"
+
+//! \deprecated Do not use
+#define XML_LRM_ATTR_MIGRATE_TARGET "migrate_target"
+
+//! \deprecated Use \c PCMK_XA_SCORE instead
+#define XML_RULE_ATTR_SCORE PCMK_XA_SCORE
+
+//! \deprecated Use \c PCMK_XA_SCORE_ATTRIBUTE instead
+#define XML_RULE_ATTR_SCORE_ATTRIBUTE PCMK_XA_SCORE_ATTRIBUTE
+
+//! \deprecated Use \c PCMK_XE_ROLE instead
+#define XML_ACL_TAG_ROLE_REF PCMK_XE_ROLE
+
+//! \deprecated Use \c PCMK_XA_ROLE instead
+#define XML_RULE_ATTR_ROLE PCMK_XA_ROLE
+
+//! \deprecated Use \c PCMK_XA_BOOLEAN_OP instead
+#define XML_RULE_ATTR_BOOLEAN_OP PCMK_XA_BOOLEAN_OP
+
+//! \deprecated Use \c PCMK_XA_ATTRIBUTE instead
+#define XML_EXPR_ATTR_ATTRIBUTE PCMK_XA_ATTRIBUTE
+
+//! \deprecated Use \c PCMK_XA_ATTRIBUTE instead
+#define XML_ACL_ATTR_ATTRIBUTE PCMK_XA_ATTRIBUTE
+
+//! \deprecated Use \c PCMK_XA_VALUE_SOURCE instead
+#define XML_EXPR_ATTR_VALUE_SOURCE PCMK_XA_VALUE_SOURCE
+
+//! \deprecated Use \c PCMK_XA_SYMMETRICAL instead
+#define XML_CONS_ATTR_SYMMETRICAL PCMK_XA_SYMMETRICAL
+
+//! \deprecated Use \c PCMK_XA_RESOURCE_DISCOVERY instead
+#define XML_LOCATION_ATTR_DISCOVERY PCMK_XA_RESOURCE_DISCOVERY
+
+//! \deprecated Use \c PCMK_XE_PARAMETERS instead
+#define XML_TAG_PARAMS PCMK_XE_PARAMETERS
+
+//! \deprecated Use \c PCMK_XA_RSC instead
+#define XML_COLOC_ATTR_SOURCE PCMK_XA_RSC
+
+//! \deprecated Use \c PCMK_XA_RSC instead
+#define XML_LOC_ATTR_SOURCE PCMK_XA_RSC
+
+//! \deprecated Use \c PCMK_XA_RSC_ROLE instead
+#define XML_COLOC_ATTR_SOURCE_ROLE PCMK_XA_RSC_ROLE
+
+//! \deprecated Use \c PCMK_XA_WITH_RSC instead
+#define XML_COLOC_ATTR_TARGET PCMK_XA_WITH_RSC
+
+//! \deprecated Use \c PCMK_XA_WITH_RSC_ROLE instead
+#define XML_COLOC_ATTR_TARGET_ROLE PCMK_XA_WITH_RSC_ROLE
+
+//! \deprecated Use \c PCMK_XA_NODE_ATTRIBUTE instead
+#define XML_COLOC_ATTR_NODE_ATTR PCMK_XA_NODE_ATTRIBUTE
+
+//! \deprecated Use \c PCMK_XA_INFLUENCE instead
+#define XML_COLOC_ATTR_INFLUENCE PCMK_XA_INFLUENCE
+
+//! \deprecated Use \c PCMK_XA_RSC_PATTERN instead
+#define XML_LOC_ATTR_SOURCE_PATTERN PCMK_XA_RSC_PATTERN
+
+//! \deprecated Use \c PCMK_XA_FIRST instead
+#define XML_ORDER_ATTR_FIRST PCMK_XA_FIRST
+
+//! \deprecated Use \c PCMK_XA_THEN instead
+#define XML_ORDER_ATTR_THEN PCMK_XA_THEN
+
+//! \deprecated Use \c PCMK_XA_FIRST_ACTION instead
+#define XML_ORDER_ATTR_FIRST_ACTION PCMK_XA_FIRST_ACTION
+
+//! \deprecated Use \c PCMK_XA_THEN_ACTION instead
+#define XML_ORDER_ATTR_THEN_ACTION PCMK_XA_THEN_ACTION
+
+//! \deprecated Use \c PCMK_XA_KIND instead
+#define XML_ORDER_ATTR_KIND PCMK_XA_KIND
+
+//! \deprecated Use \c PCMK_XA_KIND instead
+#define XML_ACL_ATTR_KIND PCMK_XA_KIND
+
+//! \deprecated Use \c PCMK_XA_TICKET instead
+#define XML_TICKET_ATTR_TICKET PCMK_XA_TICKET
+
+//! \deprecated Use \c PCMK_XA_LOSS_POLICY instead
+#define XML_TICKET_ATTR_LOSS_POLICY PCMK_XA_LOSS_POLICY
+
+//! \deprecated Do not use
+#define XML_ACL_ATTR_REFv1 "ref"
+
+//! \deprecated Use \c PCMK_XA_OBJECT_TYPE instead
+#define XML_ACL_ATTR_TAG PCMK_XA_OBJECT_TYPE
+
+//! \deprecated Do not use
+#define XML_ACL_ATTR_TAGv1 "tag"
+
+//! \deprecated Use \c PCMK_XA_XPATH instead
+#define XML_ACL_ATTR_XPATH PCMK_XA_XPATH
+
+//! \deprecated Do not use
+#define XML_CRM_TAG_PING "ping_response"
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use \c PCMK_XE_CIB instead
+#define XML_TAG_CIB PCMK_XE_CIB
+
+//! \deprecated Use \c PCMK_XE_CONFIGURATION instead
+#define XML_CIB_TAG_CONFIGURATION PCMK_XE_CONFIGURATION
+
+//! \deprecated Use \c PCMK_XE_STATUS instead
+#define XML_CIB_TAG_STATUS PCMK_XE_STATUS
+
+//! \deprecated Use \c PCMK_XE_RESOURCES instead
+#define XML_CIB_TAG_RESOURCES PCMK_XE_RESOURCES
+
+//! \deprecated Use \c PCMK_XE_NODES instead
+#define XML_CIB_TAG_NODES PCMK_XE_NODES
+
+//! \deprecated Use \c PCMK_XE_CONSTRAINTS instead
+#define XML_CIB_TAG_CONSTRAINTS PCMK_XE_CONSTRAINTS
+
+//! \deprecated Use \c PCMK_XE_CRM_CONFIG instead
+#define XML_CIB_TAG_CRMCONFIG PCMK_XE_CRM_CONFIG
+
+//! \deprecated Use \c PCMK_XE_OP_DEFAULTS instead
+#define XML_CIB_TAG_OPCONFIG PCMK_XE_OP_DEFAULTS
+
+//! \deprecated Use \c PCMK_XE_RSC_DEFAULTS instead
+#define XML_CIB_TAG_RSCCONFIG PCMK_XE_RSC_DEFAULTS
+
+//! \deprecated Use \c PCMK_XE_ACLS instead
+#define XML_CIB_TAG_ACLS PCMK_XE_ACLS
+
+//! \deprecated Use \c PCMK_XE_ALERTS instead
+#define XML_CIB_TAG_ALERTS PCMK_XE_ALERTS
+
+//! \deprecated Use \c PCMK_XE_ALERT instead
+#define XML_CIB_TAG_ALERT PCMK_XE_ALERT
+
+//! \deprecated Use \c PCMK_XE_RECIPIENT instead
+#define XML_CIB_TAG_ALERT_RECIPIENT PCMK_XE_RECIPIENT
+
+//! \deprecated Use \c PCMK_XE_SELECT instead
+#define XML_CIB_TAG_ALERT_SELECT PCMK_XE_SELECT
+
+//! \deprecated Use \c PCMK_XE_SELECT_ATTRIBUTES instead
+#define XML_CIB_TAG_ALERT_ATTRIBUTES PCMK_XE_SELECT_ATTRIBUTES
+
+//! \deprecated Use \c PCMK_XE_SELECT_FENCING instead
+#define XML_CIB_TAG_ALERT_FENCING PCMK_XE_SELECT_FENCING
+
+//! \deprecated Use \c PCMK_XE_SELECT_NODES instead
+#define XML_CIB_TAG_ALERT_NODES PCMK_XE_SELECT_NODES
+
+//! \deprecated Use \c PCMK_XE_SELECT_RESOURCES instead
+#define XML_CIB_TAG_ALERT_RESOURCES PCMK_XE_SELECT_RESOURCES
+
+//! \deprecated Use \c PCMK_XE_ATTRIBUTE instead
+#define XML_CIB_TAG_ALERT_ATTR PCMK_XE_ATTRIBUTE
+
+//! \deprecated Do not use
+#define XML_CIB_TAG_STATE "node_state"
+
+//! \deprecated Use \c PCMK_XE_NODE instead
+#define XML_CIB_TAG_NODE PCMK_XE_NODE
+
+//! \deprecated Use \c PCMK_XE_NVPAIR instead
+#define XML_CIB_TAG_NVPAIR PCMK_XE_NVPAIR
+
+//! \deprecated Use \c PCMK_XE_CLUSTER_PROPERTY_SET instead
+#define XML_CIB_TAG_PROPSET PCMK_XE_CLUSTER_PROPERTY_SET
+
+//! \deprecated Use \c PCMK_XE_INSTANCE_ATTRIBUTES instead
+#define XML_TAG_ATTR_SETS PCMK_XE_INSTANCE_ATTRIBUTES
+
+//! \deprecated Use \c PCMK_XE_META_ATTRIBUTES instead
+#define XML_TAG_META_SETS PCMK_XE_META_ATTRIBUTES
+
+//! \deprecated Do not use
+#define XML_TAG_ATTRS "attributes"
+
+//! \deprecated Do not use
+#define XML_TAG_PARAM "param"
+
+//! \deprecated Use \c PCMK_XE_UTILIZATION instead
+#define XML_TAG_UTILIZATION PCMK_XE_UTILIZATION
+
+//! \deprecated Use \c PCMK_XE_RESOURCE_REF instead
+#define XML_TAG_RESOURCE_REF PCMK_XE_RESOURCE_REF
+
+//! \deprecated Use \c PCMK_XE_PRIMITIVE instead
+#define XML_CIB_TAG_RESOURCE PCMK_XE_PRIMITIVE
+
+//! \deprecated Use \c PCMK_XE_GROUP instead
+#define XML_CIB_TAG_GROUP PCMK_XE_GROUP
+
+//! \deprecated Use \c PCMK_XE_CLONE instead
+#define XML_CIB_TAG_INCARNATION PCMK_XE_CLONE
+
+//! \deprecated Use \c PCMK_XE_BUNDLE instead
+#define XML_CIB_TAG_CONTAINER PCMK_XE_BUNDLE
+
+//! \deprecated Use \c PCMK_XE_TEMPLATE instead
+#define XML_CIB_TAG_RSC_TEMPLATE PCMK_XE_TEMPLATE
+
+//! \deprecated Do not use
+#define XML_CIB_TAG_LRM "lrm"
+
+//! \deprecated Do not use
+#define XML_LRM_TAG_RESOURCES "lrm_resources"
+
+//! \deprecated Do not use
+#define XML_LRM_TAG_RESOURCE "lrm_resource"
+
+//! \deprecated Do not use
+#define XML_LRM_TAG_RSC_OP "lrm_rsc_op"
+
+//! \deprecated Do not use
+#define XML_TAG_GRAPH "transition_graph"
+
+//! \deprecated Do not use
+#define XML_GRAPH_TAG_RSC_OP "rsc_op"
+
+//! \deprecated Do not use
+#define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event"
+
+//! \deprecated Do not use
+#define XML_GRAPH_TAG_CRM_EVENT "crm_event"
+
+//! \deprecated Do not use
+#define XML_GRAPH_TAG_DOWNED "downed"
+
+//! \deprecated Do not use
+#define XML_GRAPH_TAG_MAINTENANCE "maintenance"
+
+//! \deprecated Use \c PCMK_XE_RULE instead
+#define XML_TAG_RULE PCMK_XE_RULE
+
+//! \deprecated Use \c PCMK_XE_EXPRESSION instead
+#define XML_TAG_EXPRESSION PCMK_XE_EXPRESSION
+
+//! \deprecated Use \c PCMK_XE_RSC_COLOCATION instead
+#define XML_CONS_TAG_RSC_DEPEND PCMK_XE_RSC_COLOCATION
+
+//! \deprecated Use \c PCMK_XE_RSC_ORDER instead
+#define XML_CONS_TAG_RSC_ORDER PCMK_XE_RSC_ORDER
+
+//! \deprecated Use \c PCMK_XE_RSC_LOCATION instead
+#define XML_CONS_TAG_RSC_LOCATION PCMK_XE_RSC_LOCATION
+
+//! \deprecated Use \c PCMK_XE_RSC_TICKET instead
+#define XML_CONS_TAG_RSC_TICKET PCMK_XE_RSC_TICKET
+
+//! \deprecated Use \c PCMK_XE_RESOURCE_SET instead
+#define XML_CONS_TAG_RSC_SET PCMK_XE_RESOURCE_SET
+
+//! \deprecated Do not use
+#define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple"
+
+//! \deprecated Do not use
+#define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes"
+
+//! \deprecated Use \c PCMK_XE_ACL_TARGET instead
+#define XML_ACL_TAG_USER PCMK_XE_ACL_TARGET
+
+//! \deprecated Do not use
+#define XML_ACL_TAG_USERv1 "acl_user"
+
+//! \deprecated Use \c PCMK_XE_ACL_GROUP instead
+#define XML_ACL_TAG_GROUP PCMK_XE_ACL_GROUP
+
+//! \deprecated Use \c PCMK_XE_ACL_ROLE instead
+#define XML_ACL_TAG_ROLE PCMK_XE_ACL_ROLE
+
+//! \deprecated Use \c PCMK_XE_ACL_PERMISSION instead
+#define XML_ACL_TAG_PERMISSION PCMK_XE_ACL_PERMISSION
+
+//! \deprecated Do not use
+#define XML_ACL_TAG_ROLE_REFv1 "role_ref"
+
+//! \deprecated Do not use
+#define XML_ACL_TAG_READ "read"
+
+//! \deprecated Do not use
+#define XML_ACL_TAG_WRITE "write"
+
+//! \deprecated Do not use
+#define XML_ACL_TAG_DENY "deny"
+
+//! \deprecated Use \c PCMK_XE_TICKETS instead
+#define XML_CIB_TAG_TICKETS PCMK_XE_TICKETS
+
+//! \deprecated Do not use
+#define XML_CIB_TAG_TICKET_STATE "ticket_state"
+
+//! \deprecated Use \c PCMK_XE_TAGS instead
+#define XML_CIB_TAG_TAGS PCMK_XE_TAGS
+
+//! \deprecated Use \c PCMK_XE_TAG instead
+#define XML_CIB_TAG_TAG PCMK_XE_TAG
+
+//! \deprecated Use \c PCMK_XE_OBJ_REF instead
+#define XML_CIB_TAG_OBJ_REF PCMK_XE_OBJ_REF
+
+//! \deprecated Use \c PCMK_XE_FENCING_TOPOLOGY instead
+#define XML_TAG_FENCING_TOPOLOGY PCMK_XE_FENCING_TOPOLOGY
+
+//! \deprecated Use \c PCMK_XE_FENCING_LEVEL instead
+#define XML_TAG_FENCING_LEVEL PCMK_XE_FENCING_LEVEL
+
+//! \deprecated Use \c PCMK_XE_DIFF instead
+#define XML_TAG_DIFF PCMK_XE_DIFF
+
+//! \deprecated Use \c PCMK_XE_VERSION instead
+#define XML_DIFF_VERSION PCMK_XE_VERSION
+
+//! \deprecated Use \c PCMK_XE_SOURCE instead
+#define XML_DIFF_VSOURCE PCMK_XE_SOURCE
+
+//! \deprecated Use \c PCMK_XE_TARGET instead
+#define XML_DIFF_VTARGET PCMK_XE_TARGET
+
+//! \deprecated Use \c PCMK_XE_CHANGE instead
+#define XML_DIFF_CHANGE PCMK_XE_CHANGE
+
+//! \deprecated Use \c PCMK_XE_CHANGE_LIST instead
+#define XML_DIFF_LIST PCMK_XE_CHANGE_LIST
+
+//! \deprecated Use \c PCMK_XE_CHANGE_ATTR instead
+#define XML_DIFF_ATTR PCMK_XE_CHANGE_ATTR
+
+//! \deprecated Use \c PCMK_XE_CHANGE_RESULT instead
+#define XML_DIFF_RESULT PCMK_XE_CHANGE_RESULT
+
+//! \deprecated Use \c PCMK_XE_POSITION instead
+#define XML_DIFF_POSITION PCMK_XE_POSITION
+
+//! \deprecated Do not use
+#define F_CRM_DATA "crm_xml"
+
+//! \deprecated Do not use
+#define XML_DIFF_MARKER "__crm_diff_marker__"
+
+//! \deprecated Do not use
+#define XML_TAG_FAILED "failed"
+
+//! \deprecated Do not use
+#define XML_TAG_OPTIONS "options"
+
+//! \deprecated Do not use
+#define XML_FAIL_TAG_CIB "failed_update"
+
+//! \deprecated Use \c PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS instead
+#define CIB_OPTIONS_FIRST PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_INIT "init"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS "starting_daemons"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_WAITPING "wait_for_ping"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_RUNNING "running"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN "shutting_down"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE "shutdown_complete"
+
+//! \deprecated Do not use
+#define XML_PING_ATTR_PACEMAKERDSTATE_REMOTE "remote"
+
+#ifndef T_CRM
+//! \deprecated Do not use
+#define T_CRM "crmd"
+#endif
+
+#ifndef T_ATTRD
+//! \deprecated Do not use
+#define T_ATTRD "attrd"
+#endif
+
+//! \deprecated Do not use
+#define XML_CIB_TAG_SECTION_ALL "all"
+
+//! \deprecated Do not use
+#define XML_NODE_IS_REMOTE "remote_node"
+
+//! \deprecated Do not use
+#define XML_NODE_IS_FENCED "node_fenced"
+
+//! \deprecated Do not use
+#define XML_NODE_IS_MAINTENANCE "node_in_maintenance"
+
+//! \deprecated Do not use
+#define XML_CIB_ATTR_SHUTDOWN "shutdown"
+
+//! \deprecated Do not use
+#define XML_NODE_ATTR_RSC_DISCOVERY "resource-discovery-enabled"
+
+//! \deprecated Do not use
+#define ID(x) crm_element_value(x, PCMK_XA_ID)
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/pengine/Makefile.am b/include/crm/pengine/Makefile.am
index 3560d24..77bc28e 100644
--- a/include/crm/pengine/Makefile.am
+++ b/include/crm/pengine/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2006-2023 the Pacemaker project contributors
+# Copyright 2006-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -19,4 +19,5 @@ header_HEADERS = common.h \
status.h \
common_compat.h \
pe_types_compat.h \
- rules_compat.h
+ rules_compat.h \
+ status_compat.h
diff --git a/include/crm/pengine/common.h b/include/crm/pengine/common.h
index 2feac8a..57005e1 100644
--- a/include/crm/pengine/common.h
+++ b/include/crm/pengine/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -19,40 +19,6 @@
extern "C" {
#endif
-extern gboolean was_processing_error;
-extern gboolean was_processing_warning;
-
-const char *task2text(enum action_tasks task);
-enum action_tasks text2task(const char *task);
-enum rsc_role_e text2role(const char *role);
-const char *role2text(enum rsc_role_e role);
-const char *fail2text(enum action_fail_response fail);
-
-const char *pe_pref(GHashTable * options, const char *name);
-
-/*!
- * \brief Get readable description of a recovery type
- *
- * \param[in] type Recovery type
- *
- * \return Static string describing \p type
- */
-static inline const char *
-recovery2text(enum rsc_recovery_type type)
-{
- switch (type) {
- 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";
-}
-
typedef struct pe_re_match_data {
char *string;
int nregs;
@@ -78,7 +44,7 @@ typedef struct pe_op_eval_data {
typedef struct pe_rule_eval_data {
GHashTable *node_hash; // Only used with g_hash_table_lookup()
- enum rsc_role_e role;
+ enum rsc_role_e role; //!< \deprecated Ignored
crm_time_t *now; // @COMPAT could be const
pe_match_data_t *match_data; // @COMPAT could be const
pe_rsc_eval_data_t *rsc_data; // @COMPAT could be const
diff --git a/include/crm/pengine/common_compat.h b/include/crm/pengine/common_compat.h
index 4330ccf..621df4b 100644
--- a/include/crm/pengine/common_compat.h
+++ b/include/crm/pengine/common_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -28,20 +28,20 @@ extern "C" {
//! \deprecated Use (pcmk_role_promoted + 1) instead
#define RSC_ROLE_MAX (pcmk_role_promoted + 1)
-//! \deprecated Use role2text(pcmk_role_unknown) instead
-#define RSC_ROLE_UNKNOWN_S role2text(pcmk_role_unknown)
+//! \deprecated Use pcmk_role_text(pcmk_role_unknown) instead
+#define RSC_ROLE_UNKNOWN_S pcmk_role_text(pcmk_role_unknown)
-//! \deprecated Use role2text(pcmk_role_stopped) instead
-#define RSC_ROLE_STOPPED_S role2text(pcmk_role_stopped)
+//! \deprecated Use pcmk_role_text(pcmk_role_stopped) instead
+#define RSC_ROLE_STOPPED_S pcmk_role_text(pcmk_role_stopped)
-//! \deprecated Use role2text(pcmk_role_started) instead
-#define RSC_ROLE_STARTED_S role2text(pcmk_role_started)
+//! \deprecated Use pcmk_role_text(pcmk_role_started) instead
+#define RSC_ROLE_STARTED_S pcmk_role_text(pcmk_role_started)
-//! \deprecated Use role2text(pcmk_role_unpromoted) instead
-#define RSC_ROLE_UNPROMOTED_S role2text(pcmk_role_unpromoted)
+//! \deprecated Use pcmk_role_text(pcmk_role_unpromoted) instead
+#define RSC_ROLE_UNPROMOTED_S pcmk_role_text(pcmk_role_unpromoted)
-//! \deprecated Use role2text(pcmk_role_promoted) instead
-#define RSC_ROLE_PROMOTED_S role2text(pcmk_role_promoted)
+//! \deprecated Use pcmk_role_text(pcmk_role_promoted) instead
+#define RSC_ROLE_PROMOTED_S pcmk_role_text(pcmk_role_promoted)
//! \deprecated Do not use
#define RSC_ROLE_UNPROMOTED_LEGACY_S "Slave"
@@ -55,6 +55,41 @@ extern "C" {
//! \deprecated Do not use
#define RSC_ROLE_MASTER_S RSC_ROLE_PROMOTED_LEGACY_S
+//! \deprecated Use pcmk_role_text() instead
+const char *role2text(enum rsc_role_e role);
+
+//! \deprecated Use pcmk_parse_role() instead
+enum rsc_role_e text2role(const char *role);
+
+//! \deprecated Use pcmk_action_text() instead
+const char *task2text(enum action_tasks task);
+
+//! \deprecated Use pcmk_parse_action() instead
+enum action_tasks text2task(const char *task);
+
+//! \deprecated Use pcmk_on_fail_text() instead
+const char *fail2text(enum action_fail_response fail);
+
+//! \deprecated Do not use
+static inline const char *
+recovery2text(enum rsc_recovery_type type)
+{
+ switch (type) {
+ 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";
+}
+
+//! \deprecated Do not use
+const char *pe_pref(GHashTable * options, const char *name);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/pengine/complex.h b/include/crm/pengine/complex.h
index 9b6ad1b..e194e5e 100644
--- a/include/crm/pengine/complex.h
+++ b/include/crm/pengine/complex.h
@@ -18,6 +18,8 @@
extern "C" {
#endif
+// @COMPAT Make internal when we can break API backward compatibility
+//! \deprecated Do not use
extern pcmk_rsc_methods_t resource_class_functions[];
GHashTable *pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node,
diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h
index 9c8068f..caf1e21 100644
--- a/include/crm/pengine/internal.h
+++ b/include/crm/pengine/internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -13,7 +13,7 @@
# include <stdbool.h>
# include <stdint.h>
# include <string.h>
-# include <crm/msg_xml.h>
+# include <crm/common/xml.h>
# include <crm/pengine/status.h>
# include <crm/pengine/remote_internal.h>
# include <crm/common/internal.h>
@@ -31,153 +31,6 @@ bool pe__clone_flag_is_set(const pcmk_resource_t *clone, uint32_t flags);
bool pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags);
pcmk_resource_t *pe__last_group_member(const pcmk_resource_t *group);
-
-# define pe_rsc_info(rsc, fmt, args...) crm_log_tag(LOG_INFO, rsc ? rsc->id : "<NULL>", fmt, ##args)
-# define pe_rsc_debug(rsc, fmt, args...) crm_log_tag(LOG_DEBUG, rsc ? rsc->id : "<NULL>", fmt, ##args)
-# define pe_rsc_trace(rsc, fmt, args...) crm_log_tag(LOG_TRACE, rsc ? rsc->id : "<NULL>", fmt, ##args)
-
-# define pe_err(fmt...) do { \
- was_processing_error = TRUE; \
- pcmk__config_err(fmt); \
- } while (0)
-
-# define pe_warn(fmt...) do { \
- was_processing_warning = TRUE; \
- pcmk__config_warn(fmt); \
- } while (0)
-
-# define pe_proc_err(fmt...) { was_processing_error = TRUE; crm_err(fmt); }
-# define pe_proc_warn(fmt...) { was_processing_warning = TRUE; crm_warn(fmt); }
-
-#define pe__set_working_set_flags(scheduler, flags_to_set) do { \
- (scheduler)->flags = pcmk__set_flags_as(__func__, __LINE__, \
- LOG_TRACE, "Scheduler", crm_system_name, \
- (scheduler)->flags, (flags_to_set), #flags_to_set); \
- } while (0)
-
-#define pe__clear_working_set_flags(scheduler, flags_to_clear) do { \
- (scheduler)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
- LOG_TRACE, "Scheduler", crm_system_name, \
- (scheduler)->flags, (flags_to_clear), #flags_to_clear); \
- } while (0)
-
-#define pe__set_resource_flags(resource, flags_to_set) do { \
- (resource)->flags = pcmk__set_flags_as(__func__, __LINE__, \
- LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \
- (flags_to_set), #flags_to_set); \
- } while (0)
-
-#define pe__clear_resource_flags(resource, flags_to_clear) do { \
- (resource)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
- LOG_TRACE, "Resource", (resource)->id, (resource)->flags, \
- (flags_to_clear), #flags_to_clear); \
- } while (0)
-
-#define pe__set_action_flags(action, flags_to_set) do { \
- (action)->flags = pcmk__set_flags_as(__func__, __LINE__, \
- LOG_TRACE, \
- "Action", (action)->uuid, \
- (action)->flags, \
- (flags_to_set), \
- #flags_to_set); \
- } while (0)
-
-#define pe__clear_action_flags(action, flags_to_clear) do { \
- (action)->flags = pcmk__clear_flags_as(__func__, __LINE__, \
- LOG_TRACE, \
- "Action", (action)->uuid, \
- (action)->flags, \
- (flags_to_clear), \
- #flags_to_clear); \
- } while (0)
-
-#define pe__set_raw_action_flags(action_flags, action_name, flags_to_set) do { \
- action_flags = pcmk__set_flags_as(__func__, __LINE__, \
- LOG_TRACE, "Action", action_name, \
- (action_flags), \
- (flags_to_set), #flags_to_set); \
- } while (0)
-
-#define pe__clear_raw_action_flags(action_flags, action_name, flags_to_clear) do { \
- action_flags = pcmk__clear_flags_as(__func__, __LINE__, \
- LOG_TRACE, \
- "Action", action_name, \
- (action_flags), \
- (flags_to_clear), \
- #flags_to_clear); \
- } while (0)
-
-#define pe__set_action_flags_as(function, line, action, flags_to_set) do { \
- (action)->flags = pcmk__set_flags_as((function), (line), \
- LOG_TRACE, \
- "Action", (action)->uuid, \
- (action)->flags, \
- (flags_to_set), \
- #flags_to_set); \
- } while (0)
-
-#define pe__clear_action_flags_as(function, line, action, flags_to_clear) do { \
- (action)->flags = pcmk__clear_flags_as((function), (line), \
- LOG_TRACE, \
- "Action", (action)->uuid, \
- (action)->flags, \
- (flags_to_clear), \
- #flags_to_clear); \
- } while (0)
-
-#define pe__set_order_flags(order_flags, flags_to_set) do { \
- order_flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE, \
- "Ordering", "constraint", \
- order_flags, (flags_to_set), \
- #flags_to_set); \
- } while (0)
-
-#define pe__clear_order_flags(order_flags, flags_to_clear) do { \
- order_flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \
- "Ordering", "constraint", \
- order_flags, (flags_to_clear), \
- #flags_to_clear); \
- } while (0)
-
-#define pe_warn_once(pe_wo_bit, fmt...) do { \
- if (!pcmk_is_set(pcmk__warnings, pe_wo_bit)) { \
- if (pe_wo_bit == pcmk__wo_blind) { \
- crm_warn(fmt); \
- } else { \
- pe_warn(fmt); \
- } \
- pcmk__warnings = pcmk__set_flags_as(__func__, __LINE__, \
- LOG_TRACE, \
- "Warn-once", "logging", \
- pcmk__warnings, \
- (pe_wo_bit), #pe_wo_bit); \
- } \
- } while (0);
-
-
-typedef struct pe__location_constraint_s {
- char *id; // Constraint XML ID
- pcmk_resource_t *rsc_lh; // Resource being located
- enum rsc_role_e role_filter; // Role to locate
- enum pe_discover_e discover_mode; // Resource discovery
- GList *node_list_rh; // List of pcmk_node_t*
-} pe__location_t;
-
-typedef struct pe__order_constraint_s {
- int id;
- uint32_t flags; // Group of enum pcmk__action_relation_flags
-
- void *lh_opaque;
- pcmk_resource_t *lh_rsc;
- pcmk_action_t *lh_action;
- char *lh_action_task;
-
- void *rh_opaque;
- pcmk_resource_t *rh_rsc;
- pcmk_action_t *rh_action;
- char *rh_action_task;
-} pe__ordering_t;
-
const pcmk_resource_t *pe__const_top_resource(const pcmk_resource_t *rsc,
bool include_bundle);
@@ -201,16 +54,10 @@ void pe__create_promotable_pseudo_ops(pcmk_resource_t *clone,
bool pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node);
-void add_hash_param(GHashTable * hash, const char *name, const char *value);
-
char *native_parameter(pcmk_resource_t *rsc, pcmk_node_t *node, gboolean create,
const char *name, pcmk_scheduler_t *scheduler);
pcmk_node_t *native_location(const pcmk_resource_t *rsc, GList **list,
int current);
-
-void pe_metadata(pcmk__output_t *out);
-void verify_pe_options(GHashTable * options);
-
void native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node,
pcmk_scheduler_t *scheduler, gboolean failed);
@@ -247,8 +94,8 @@ gchar *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);
-int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name
- , size_t pairs_count, ...);
+int pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,
+ ...) G_GNUC_NULL_TERMINATED;
char *pe__node_display_name(pcmk_node_t *node, bool print_detail);
@@ -258,12 +105,7 @@ void pe__order_notifs_after_fencing(const pcmk_action_t *action,
pcmk_action_t *stonith_op);
-static inline const char *
-pe__rsc_bool_str(const pcmk_resource_t *rsc, uint64_t rsc_flag)
-{
- return pcmk__btoa(pcmk_is_set(rsc->flags, rsc_flag));
-}
-
+// Resource output methods
int pe__clone_xml(pcmk__output_t *out, va_list args);
int pe__clone_default(pcmk__output_t *out, va_list args);
int pe__group_xml(pcmk__output_t *out, va_list args);
@@ -319,13 +161,6 @@ bool pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node,
pcmk_node_t *pe__find_active_requires(const pcmk_resource_t *rsc,
unsigned int *count);
-static inline pcmk_node_t *
-pe__current_node(const pcmk_resource_t *rsc)
-{
- return (rsc == NULL)? NULL : rsc->fns->active_node(rsc, NULL, NULL);
-}
-
-
/* Binary like operators for lists of nodes */
GHashTable *pe__node_list2table(const GList *list);
@@ -396,9 +231,6 @@ pcmk_action_t *custom_action(pcmk_resource_t *rsc, char *key, const char *task,
rsc, demote_key(rsc), PCMK_ACTION_DEMOTE, node, \
optional, rsc->cluster)
-extern int pe_get_configured_timeout(pcmk_resource_t *rsc, const char *action,
- pcmk_scheduler_t *scheduler);
-
pcmk_action_t *find_first_action(const GList *input, const char *uuid,
const char *task, const pcmk_node_t *on_node);
@@ -451,17 +283,7 @@ int pe__target_rc_from_xml(const xmlNode *xml_op);
gint pe__cmp_node_name(gconstpointer a, gconstpointer b);
bool is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any);
-typedef struct op_digest_cache_s {
- enum pcmk__digest_result rc;
- xmlNode *params_all;
- xmlNode *params_secure;
- xmlNode *params_restart;
- char *digest_all_calc;
- char *digest_secure_calc;
- char *digest_restart_calc;
-} op_digest_cache_t;
-
-op_digest_cache_t *pe__calculate_digests(pcmk_resource_t *rsc, const char *task,
+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,
@@ -471,7 +293,7 @@ op_digest_cache_t *pe__calculate_digests(pcmk_resource_t *rsc, const char *task,
void pe__free_digests(gpointer ptr);
-op_digest_cache_t *rsc_action_digest_cmp(pcmk_resource_t *rsc,
+pcmk__op_digest_t *rsc_action_digest_cmp(pcmk_resource_t *rsc,
const xmlNode *xml_op,
pcmk_node_t *node,
pcmk_scheduler_t *scheduler);
@@ -515,17 +337,6 @@ int pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
const char *name, const pcmk_node_t *node,
unsigned int options);
-//! A single instance of a bundle
-typedef struct {
- int offset; //!< 0-origin index of this instance in bundle
- char *ipaddr; //!< IP address associated with this instance
- pcmk_node_t *node; //!< Node created for this instance
- pcmk_resource_t *ip; //!< IP address resource for ipaddr
- pcmk_resource_t *child; //!< Instance of bundled resource
- pcmk_resource_t *container; //!< Container associated with this instance
- pcmk_resource_t *remote; //!< Pacemaker Remote connection into container
-} pe__bundle_replica_t;
-
GList *pe__bundle_containers(const pcmk_resource_t *bundle);
int pe__bundle_max(const pcmk_resource_t *rsc);
@@ -535,25 +346,17 @@ pcmk_resource_t *pe__bundled_resource(const pcmk_resource_t *rsc);
const pcmk_resource_t *pe__get_rsc_in_container(const pcmk_resource_t *instance);
pcmk_resource_t *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);
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);
pcmk_resource_t *pe__find_bundle_replica(const pcmk_resource_t *bundle,
const pcmk_node_t *node);
bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc);
-const char *pe__add_bundle_remote_name(pcmk_resource_t *rsc,
- pcmk_scheduler_t *scheduler,
- xmlNode *xml, const char *field);
-
-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);
-const char *pe_node_attribute_raw(const pcmk_node_t *node, const char *name);
+const char *pe__add_bundle_remote_name(pcmk_resource_t *rsc, xmlNode *xml,
+ const char *field);
bool pe__is_universal_clone(const pcmk_resource_t *rsc,
const pcmk_scheduler_t *scheduler);
void pe__add_param_check(const xmlNode *rsc_op, pcmk_resource_t *rsc,
@@ -621,81 +424,18 @@ int pe__node_health(pcmk_node_t *node);
static inline enum pcmk__health_strategy
pe__health_strategy(pcmk_scheduler_t *scheduler)
{
- return pcmk__parse_health_strategy(pe_pref(scheduler->config_hash,
- PCMK__OPT_NODE_HEALTH_STRATEGY));
+ const char *strategy = pcmk__cluster_option(scheduler->config_hash,
+ PCMK_OPT_NODE_HEALTH_STRATEGY);
+
+ return pcmk__parse_health_strategy(strategy);
}
static inline int
pe__health_score(const char *option, pcmk_scheduler_t *scheduler)
{
- return char2score(pe_pref(scheduler->config_hash, option));
-}
-
-/*!
- * \internal
- * \brief Return a string suitable for logging as a node name
- *
- * \param[in] node Node to return a node name string for
- *
- * \return Node name if available, otherwise node ID if available,
- * otherwise "unspecified node" if node is NULL or "unidentified node"
- * if node has neither a name nor ID.
- */
-static inline const char *
-pe__node_name(const pcmk_node_t *node)
-{
- if (node == NULL) {
- return "unspecified node";
-
- } else if (node->details->uname != NULL) {
- return node->details->uname;
-
- } else if (node->details->id != NULL) {
- return node->details->id;
-
- } else {
- return "unidentified node";
- }
-}
-
-/*!
- * \internal
- * \brief Check whether two node objects refer to the same node
- *
- * \param[in] node1 First node object to compare
- * \param[in] node2 Second node object to compare
- *
- * \return true if \p node1 and \p node2 refer to the same node
- */
-static inline bool
-pe__same_node(const pcmk_node_t *node1, const pcmk_node_t *node2)
-{
- return (node1 != NULL) && (node2 != NULL)
- && (node1->details == node2->details);
-}
+ const char *value = pcmk__cluster_option(scheduler->config_hash, option);
-/*!
- * \internal
- * \brief Get the operation key from an action history entry
- *
- * \param[in] xml Action history entry
- *
- * \return Entry's operation key
- */
-static inline const char *
-pe__xe_history_key(const xmlNode *xml)
-{
- if (xml == NULL) {
- return NULL;
- } else {
- /* @COMPAT Pacemaker <= 1.1.5 did not add the key, and used the ID
- * instead. Checking for that allows us to process old saved CIBs,
- * including some regression tests.
- */
- const char *key = crm_element_value(xml, XML_LRM_ATTR_TASK_KEY);
-
- return pcmk__str_empty(key)? ID(xml) : key;
- }
+ return char2score(value);
}
#endif
diff --git a/include/crm/pengine/pe_types_compat.h b/include/crm/pengine/pe_types_compat.h
index 1becd12..ccd0a5f 100644
--- a/include/crm/pengine/pe_types_compat.h
+++ b/include/crm/pengine/pe_types_compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -25,64 +25,65 @@ extern "C" {
* release.
*/
-//! \deprecated Use pcmk_rsc_removed instead
+//! \deprecated Do not use
#define pe_rsc_orphan pcmk_rsc_removed
-//! \deprecated Use pcmk_rsc_managed instead
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use
#define pe_rsc_managed pcmk_rsc_managed
-//! \deprecated Use pcmk_rsc_blocked instead
+//! \deprecated Do not use
#define pe_rsc_block pcmk_rsc_blocked
-//! \deprecated Use pcmk_rsc_removed_filler instead
+//! \deprecated Do not use
#define pe_rsc_orphan_container_filler pcmk_rsc_removed_filler
-//! \deprecated Use pcmk_rsc_notify instead
+//! \deprecated Do not use
#define pe_rsc_notify pcmk_rsc_notify
-//! \deprecated Use pcmk_rsc_unique instead
+//! \deprecated Do not use
#define pe_rsc_unique pcmk_rsc_unique
-//! \deprecated Use pcmk_rsc_fence_device instead
+//! \deprecated Do not use
#define pe_rsc_fence_device pcmk_rsc_fence_device
-//! \deprecated Use pcmk_rsc_promotable instead
+//! \deprecated Do not use
#define pe_rsc_promotable pcmk_rsc_promotable
-//! \deprecated Use pcmk_rsc_unassigned instead
+//! \deprecated Do not use
#define pe_rsc_provisional pcmk_rsc_unassigned
-//! \deprecated Use pcmk_rsc_assigning instead
+//! \deprecated Do not use
#define pe_rsc_allocating pcmk_rsc_assigning
-//! \deprecated Use pcmk_rsc_updating_nodes instead
+//! \deprecated Do not use
#define pe_rsc_merging pcmk_rsc_updating_nodes
-//! \deprecated Use pcmk_rsc_restarting instead
+//! \deprecated Do not use
#define pe_rsc_restarting pcmk_rsc_restarting
-//! \deprecated Use pcmk_rsc_stop_if_failed instead
+//! \deprecated Do not use
#define pe_rsc_stop pcmk_rsc_stop_if_failed
-//! \deprecated Use pcmk_rsc_reload instead
+//! \deprecated Do not use
#define pe_rsc_reload pcmk_rsc_reload
-//! \deprecated Use pcmk_rsc_remote_nesting_allowed instead
+//! \deprecated Do not use
#define pe_rsc_allow_remote_remotes pcmk_rsc_remote_nesting_allowed
-//! \deprecated Use pcmk_rsc_critical instead
+//! \deprecated Do not use
#define pe_rsc_critical pcmk_rsc_critical
-//! \deprecated Use pcmk_rsc_failed instead
+//! \deprecated Do not use
#define pe_rsc_failed pcmk_rsc_failed
-//! \deprecated Use pcmk_rsc_detect_loop instead
+//! \deprecated Do not use
#define pe_rsc_detect_loop pcmk_rsc_detect_loop
//! \deprecated Do not use
#define pe_rsc_runnable pcmk_rsc_runnable
-//! \deprecated Use pcmk_rsc_start_pending instead
+//! \deprecated Do not use
#define pe_rsc_start_pending pcmk_rsc_start_pending
//!< \deprecated Do not use
@@ -91,106 +92,108 @@ extern "C" {
//!< \deprecated Do not use
#define pe_rsc_stopping pcmk_rsc_stopping
-//! \deprecated Use pcmk_rsc_stop_unexpected instead
+//! \deprecated Do not use
#define pe_rsc_stop_unexpected pcmk_rsc_stop_unexpected
-//! \deprecated Use pcmk_rsc_migratable instead
+//! \deprecated Do not use
#define pe_rsc_allow_migrate pcmk_rsc_migratable
-//! \deprecated Use pcmk_rsc_ignore_failure instead
+//! \deprecated Do not use
#define pe_rsc_failure_ignored pcmk_rsc_ignore_failure
-//! \deprecated Use pcmk_rsc_replica_container instead
+//! \deprecated Do not use
#define pe_rsc_replica_container pcmk_rsc_replica_container
-//! \deprecated Use pcmk_rsc_maintenance instead
+//! \deprecated Do not use
#define pe_rsc_maintenance pcmk_rsc_maintenance
//! \deprecated Do not use
#define pe_rsc_is_container pcmk_rsc_has_filler
-//! \deprecated Use pcmk_rsc_needs_quorum instead
+//! \deprecated Do not use
#define pe_rsc_needs_quorum pcmk_rsc_needs_quorum
-//! \deprecated Use pcmk_rsc_needs_fencing instead
+//! \deprecated Do not use
#define pe_rsc_needs_fencing pcmk_rsc_needs_fencing
-//! \deprecated Use pcmk_rsc_needs_unfencing instead
+//! \deprecated Do not use
#define pe_rsc_needs_unfencing pcmk_rsc_needs_unfencing
-//! \deprecated Use pcmk_sched_quorate instead
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use
#define pe_flag_have_quorum pcmk_sched_quorate
-//! \deprecated Use pcmk_sched_symmetric_cluster instead
+//! \deprecated Do not use
#define pe_flag_symmetric_cluster pcmk_sched_symmetric_cluster
-//! \deprecated Use pcmk_sched_in_maintenance instead
+//! \deprecated Do not use
#define pe_flag_maintenance_mode pcmk_sched_in_maintenance
-//! \deprecated Use pcmk_sched_fencing_enabled instead
+//! \deprecated Do not use
#define pe_flag_stonith_enabled pcmk_sched_fencing_enabled
-//! \deprecated Use pcmk_sched_have_fencing instead
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Do not use
#define pe_flag_have_stonith_resource pcmk_sched_have_fencing
-//! \deprecated Use pcmk_sched_enable_unfencing instead
+//! \deprecated Do not use
#define pe_flag_enable_unfencing pcmk_sched_enable_unfencing
-//! \deprecated Use pcmk_sched_concurrent_fencing instead
+//! \deprecated Do not use
#define pe_flag_concurrent_fencing pcmk_sched_concurrent_fencing
-//! \deprecated Use pcmk_sched_stop_removed_resources instead
+//! \deprecated Do not use
#define pe_flag_stop_rsc_orphans pcmk_sched_stop_removed_resources
-//! \deprecated Use pcmk_sched_cancel_removed_actions instead
+//! \deprecated Do not use
#define pe_flag_stop_action_orphans pcmk_sched_cancel_removed_actions
-//! \deprecated Use pcmk_sched_stop_all instead
+//! \deprecated Do not use
#define pe_flag_stop_everything pcmk_sched_stop_all
-//! \deprecated Use pcmk_sched_start_failure_fatal instead
+//! \deprecated Do not use
#define pe_flag_start_failure_fatal pcmk_sched_start_failure_fatal
//! \deprecated Do not use
#define pe_flag_remove_after_stop pcmk_sched_remove_after_stop
-//! \deprecated Use pcmk_sched_startup_fencing instead
+//! \deprecated Do not use
#define pe_flag_startup_fencing pcmk_sched_startup_fencing
-//! \deprecated Use pcmk_sched_shutdown_lock instead
+//! \deprecated Do not use
#define pe_flag_shutdown_lock pcmk_sched_shutdown_lock
-//! \deprecated Use pcmk_sched_probe_resources instead
+//! \deprecated Do not use
#define pe_flag_startup_probes pcmk_sched_probe_resources
-//! \deprecated Use pcmk_sched_have_status instead
+//! \deprecated Do not use
#define pe_flag_have_status pcmk_sched_have_status
-//! \deprecated Use pcmk_sched_have_remote_nodes instead
+//! \deprecated Do not use
#define pe_flag_have_remote_nodes pcmk_sched_have_remote_nodes
-//! \deprecated Use pcmk_sched_location_only instead
+//! \deprecated Do not use
#define pe_flag_quick_location pcmk_sched_location_only
-//! \deprecated Use pcmk_sched_sanitized instead
+//! \deprecated Do not use
#define pe_flag_sanitized pcmk_sched_sanitized
//! \deprecated Do not use
#define pe_flag_stdout (1ULL << 22)
-//! \deprecated Use pcmk_sched_no_counts instead
+//! \deprecated Do not use
#define pe_flag_no_counts pcmk_sched_no_counts
-//! \deprecated Use pcmk_sched_no_compat instead
+//! \deprecated Do not use
#define pe_flag_no_compat pcmk_sched_no_compat
-//! \deprecated Use pcmk_sched_output_scores instead
+//! \deprecated Do not use
#define pe_flag_show_scores pcmk_sched_output_scores
-//! \deprecated Use pcmk_sched_show_utilization instead
+//! \deprecated Do not use
#define pe_flag_show_utilization pcmk_sched_show_utilization
-//! \deprecated Use pcmk_sched_validate_only instead
+//! \deprecated Do not use
#define pe_flag_check_config pcmk_sched_validate_only
//!@{
@@ -223,6 +226,7 @@ typedef struct pe_action_wrapper_s action_wrapper_t;
//! \deprecated Do not use
typedef struct pe_action_wrapper_s pe_action_wrapper_t;
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Use pcmk_node_t instead
typedef struct pe_node_s node_t;
@@ -232,24 +236,26 @@ typedef struct pe_node_s pe_node_t;
//! \deprecated Use enum pe_quorum_policy instead
typedef enum pe_quorum_policy no_quorum_policy_t;
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated use pcmk_resource_t instead
typedef struct pe_resource_s resource_t;
//! \deprecated use pcmk_resource_t instead
typedef struct pe_resource_s pe_resource_t;
-//! \deprecated Use pcmk_tag_t instead
+//! \deprecated Do not use
typedef struct pe_tag_s tag_t;
-//! \deprecated Use pcmk_tag_t instead
+//! \deprecated Do not use
typedef struct pe_tag_s pe_tag_t;
-//! \deprecated Use pcmk_ticket_t instead
+//! \deprecated Do not use
typedef struct pe_ticket_s ticket_t;
-//! \deprecated Use pcmk_ticket_t instead
+//! \deprecated Do not use
typedef struct pe_ticket_s pe_ticket_t;
+// NOTE: sbd (as of at least 1.5.2) uses this
//! \deprecated Use pcmk_scheduler_t instead
typedef struct pe_working_set_s pe_working_set_t;
diff --git a/include/crm/pengine/remote_internal.h b/include/crm/pengine/remote_internal.h
index 0e7c044..138574c 100644
--- a/include/crm/pengine/remote_internal.h
+++ b/include/crm/pengine/remote_internal.h
@@ -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.
*
@@ -19,11 +19,6 @@ extern "C" {
#include <crm/pengine/status.h>
bool xml_contains_remote_node(xmlNode *xml);
-bool pe__is_remote_node(const pcmk_node_t *node);
-bool pe__is_guest_node(const pcmk_node_t *node);
-bool pe__is_guest_or_remote_node(const pcmk_node_t *node);
-bool pe__is_bundle_node(const pcmk_node_t *node);
-bool pe__resource_is_remote_conn(const pcmk_resource_t *rsc);
pcmk_resource_t *pe__resource_contains_guest_node(const pcmk_scheduler_t *scheduler,
const pcmk_resource_t *rsc);
void pe_foreach_guest_node(const pcmk_scheduler_t *scheduler,
diff --git a/include/crm/pengine/rules.h b/include/crm/pengine/rules.h
index 264bd69..27b9a2d 100644
--- a/include/crm/pengine/rules.h
+++ b/include/crm/pengine/rules.h
@@ -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.
*
@@ -13,41 +13,13 @@
# include <glib.h>
# include <crm/crm.h>
# include <crm/common/iso8601.h>
+# include <crm/common/scheduler.h>
# include <crm/pengine/common.h>
#ifdef __cplusplus
extern "C" {
#endif
-enum expression_type {
- not_expr = 0,
- nested_rule = 1,
- attr_expr = 2,
- loc_expr = 3,
- role_expr = 4,
- time_expr = 5,
-#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
- //! \deprecated Do not use (will be removed in a future release)
- version_expr = 6,
-#endif
- rsc_expr = 7,
- op_expr = 8,
-};
-
-enum expression_type find_expression_type(xmlNode * expr);
-
-gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash,
- crm_time_t *now, crm_time_t *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);
-
-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);
-
void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
const pe_rule_eval_data_t *rule_data, GHashTable *hash,
const char *always_first, gboolean overwrite,
@@ -59,16 +31,6 @@ void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj,
gboolean overwrite, crm_time_t *now,
crm_time_t *next_change);
-char *pe_expand_re_matches(const char *string,
- const pe_re_match_data_t *match_data);
-
-gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
- crm_time_t *next_change);
-gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
- crm_time_t *next_change);
-gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
- crm_time_t *next_change);
-
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include <crm/pengine/rules_compat.h>
#endif
diff --git a/include/crm/pengine/rules_compat.h b/include/crm/pengine/rules_compat.h
index 95fc9ac..8dcaabb 100644
--- a/include/crm/pengine/rules_compat.h
+++ b/include/crm/pengine/rules_compat.h
@@ -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.
*
@@ -28,43 +28,77 @@ extern "C" {
* release.
*/
-//! \deprecated Use pe_evaluate_rules() instead
+//! \deprecated Use pcmk_evaluate_rule() on each rule instead
+gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash,
+ crm_time_t *now, crm_time_t *next_change);
+
+//! \deprecated Use pcmk_evaluate_rule() on each rule instead
+gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
+ crm_time_t *next_change);
+
+//! \deprecated Use pcmk_evaluate_rule() on each rule instead
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now);
-//! \deprecated Use pe_test_rule() instead
+//! \deprecated Use pcmk_evaluate_rule() instead
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
crm_time_t *now);
-//! \deprecated Use pe_test_rule() instead
+//! \deprecated Use pcmk_evaluate_rule() instead
+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);
+
+//! \deprecated Use pcmk_evaluate_rule() instead
gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash,
enum rsc_role_e role, crm_time_t *now,
pe_re_match_data_t *re_match_data);
-//! \deprecated Use pe_test_rule() instead
+//! \deprecated Use pcmk_evaluate_rule() instead
gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash,
enum rsc_role_e role, crm_time_t *now,
pe_match_data_t *match_data);
-//! \deprecated Use pe_test_expression() instead
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
+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);
+
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
gboolean test_expression(xmlNode *expr, GHashTable *node_hash,
enum rsc_role_e role, crm_time_t *now);
-//! \deprecated Use pe_test_expression() instead
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash,
enum rsc_role_e role, crm_time_t *now,
pe_re_match_data_t *re_match_data);
-//! \deprecated Use pe_test_expression() instead
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
enum rsc_role_e role,
crm_time_t *now, pe_match_data_t *match_data);
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
+gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
+ crm_time_t *next_change);
+
+//! \deprecated Use pcmk_evaluate_rule() on parent rule instead
+gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
+ crm_time_t *next_change);
+
//! \deprecated Use pe_unpack_nvpairs() instead
void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj,
const char *set_name, GHashTable *node_hash,
GHashTable *hash, const char *always_first,
gboolean overwrite, crm_time_t *now);
+//! \deprecated Do not use
+enum expression_type find_expression_type(xmlNode *expr);
+
+//! \deprecated Do not use
+char *pe_expand_re_matches(const char *string,
+ const pe_re_match_data_t *match_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/crm/pengine/rules_internal.h b/include/crm/pengine/rules_internal.h
index 9b81963..d659ee0 100644
--- a/include/crm/pengine/rules_internal.h
+++ b/include/crm/pengine/rules_internal.h
@@ -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.
*
@@ -19,18 +19,4 @@
GList *pe_unpack_alerts(const xmlNode *alerts);
void pe_free_alert_list(GList *alert_list);
-gboolean pe__eval_attr_expr(const xmlNode *expr,
- const pe_rule_eval_data_t *rule_data);
-int pe__eval_date_expr(const xmlNode *expr,
- const pe_rule_eval_data_t *rule_data,
- crm_time_t *next_change);
-gboolean pe__eval_op_expr(const xmlNode *expr,
- const pe_rule_eval_data_t *rule_data);
-gboolean pe__eval_role_expr(const xmlNode *expr,
- const pe_rule_eval_data_t *rule_data);
-gboolean pe__eval_rsc_expr(const xmlNode *expr,
- const pe_rule_eval_data_t *rule_data);
-
-int pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec);
-
#endif
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
index 9c85425..037d3fa 100644
--- a/include/crm/pengine/status.h
+++ b/include/crm/pengine/status.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -29,16 +29,25 @@ extern "C" {
*/
const char *rsc_printable_id(const pcmk_resource_t *rsc);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
gboolean cluster_status(pcmk_scheduler_t *scheduler);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
pcmk_scheduler_t *pe_new_working_set(void);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
void pe_free_working_set(pcmk_scheduler_t *scheduler);
+
void set_working_set_defaults(pcmk_scheduler_t *scheduler);
void cleanup_calculations(pcmk_scheduler_t *scheduler);
+
+// NOTE: sbd (as of at least 1.5.2) uses this
void pe_reset_working_set(pcmk_scheduler_t *scheduler);
+
pcmk_resource_t *pe_find_resource(GList *rsc_list, const char *id_rh);
pcmk_resource_t *pe_find_resource_with_flags(GList *rsc_list, const char *id,
enum pe_find flags);
-pcmk_node_t *pe_find_node(const GList *node_list, const char *node_name);
pcmk_node_t *pe_find_node_id(const GList *node_list, const char *id);
pcmk_node_t *pe_find_node_any(const GList *node_list, const char *id,
const char *node_name);
@@ -48,63 +57,9 @@ void calculate_active_ops(const GList *sorted_op_list, int *start_index,
int *stop_index);
int pe_bundle_replicas(const pcmk_resource_t *rsc);
-/*!
- * \brief Check whether a resource is any clone type
- *
- * \param[in] rsc Resource to check
- *
- * \return true if resource is clone, false otherwise
- */
-static inline bool
-pe_rsc_is_clone(const pcmk_resource_t *rsc)
-{
- return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_clone);
-}
-
-/*!
- * \brief Check whether a resource is a globally unique clone
- *
- * \param[in] rsc Resource to check
- *
- * \return true if resource is unique clone, false otherwise
- */
-static inline bool
-pe_rsc_is_unique_clone(const pcmk_resource_t *rsc)
-{
- return pe_rsc_is_clone(rsc) && pcmk_is_set(rsc->flags, pcmk_rsc_unique);
-}
-
-/*!
- * \brief Check whether a resource is an anonymous clone
- *
- * \param[in] rsc Resource to check
- *
- * \return true if resource is anonymous clone, false otherwise
- */
-static inline bool
-pe_rsc_is_anon_clone(const pcmk_resource_t *rsc)
-{
- return pe_rsc_is_clone(rsc) && !pcmk_is_set(rsc->flags, pcmk_rsc_unique);
-}
-
-/*!
- * \brief Check whether a resource is part of a bundle
- *
- * \param[in] rsc Resource to check
- *
- * \return true if resource is part of a bundle, false otherwise
- */
-static inline bool
-pe_rsc_is_bundled(const pcmk_resource_t *rsc)
-{
- if (rsc == NULL) {
- return false;
- }
- while (rsc->parent != NULL) {
- rsc = rsc->parent;
- }
- return rsc->variant == pcmk_rsc_variant_bundle;
-}
+#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
+#include <crm/pengine/status_compat.h>
+#endif
#ifdef __cplusplus
}
diff --git a/include/crm/pengine/status_compat.h b/include/crm/pengine/status_compat.h
new file mode 100644
index 0000000..1ff5506
--- /dev/null
+++ b/include/crm/pengine/status_compat.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__CRM_PENGINE_STATUS_COMPAT__H
+#define PCMK__CRM_PENGINE_STATUS_COMPAT__H
+
+#include <stdbool.h> // bool
+#include <crm/common/util.h> // pcmk_is_set()
+#include <crm/common/scheduler.h> // pcmk_resource_t, pcmk_rsc_unique, etc.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file
+ * \brief Deprecated Pacemaker scheduler utilities
+ * \ingroup pengine
+ * \deprecated Do not include this header directly. The utilities in this
+ * header, and the header itself, will be removed in a future
+ * release.
+ */
+
+// NOTE: sbd (as of at least 1.5.2) uses this
+//! \deprecated Use pcmk_find_node() with scheduler object instead
+pcmk_node_t *pe_find_node(const GList *node_list, const char *node_name);
+
+//! \deprecated Compare variant directly instead
+static inline bool
+pe_rsc_is_clone(const pcmk_resource_t *rsc)
+{
+ return (rsc != NULL) && (rsc->variant == pcmk_rsc_variant_clone);
+}
+
+//! \deprecated Compare variant and flags directly
+static inline bool
+pe_rsc_is_unique_clone(const pcmk_resource_t *rsc)
+{
+ return pe_rsc_is_clone(rsc) && pcmk_is_set(rsc->flags, pcmk_rsc_unique);
+}
+
+//! \deprecated Compare variant and flags directly
+static inline bool
+pe_rsc_is_anon_clone(const pcmk_resource_t *rsc)
+{
+ return pe_rsc_is_clone(rsc) && !pcmk_is_set(rsc->flags, pcmk_rsc_unique);
+}
+
+//! \deprecated Compare ancestor variants directly
+static inline bool
+pe_rsc_is_bundled(const pcmk_resource_t *rsc)
+{
+ if (rsc == NULL) {
+ return false;
+ }
+ while (rsc->parent != NULL) {
+ rsc = rsc->parent;
+ }
+ return rsc->variant == pcmk_rsc_variant_bundle;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PCMK__CRM_PENGINE_STATUS_COMPAT__H
diff --git a/include/crm/services.h b/include/crm/services.h
index 4f0a5f0..3e66eb0 100644
--- a/include/crm/services.h
+++ b/include/crm/services.h
@@ -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,6 +26,8 @@
extern "C" {
#endif
+// NOTE: booth (as of at least 1.1) checks for the existence of this header
+
/*!
* \file
* \brief Services API
@@ -48,7 +50,10 @@ extern "C" {
enum lsb_exitcode {
PCMK_LSB_OK = 0,
+
+ // NOTE: booth (as of at least 1.1) uses this value
PCMK_LSB_UNKNOWN_ERROR = 1,
+
PCMK_LSB_INVALID_PARAM = 2,
PCMK_LSB_UNIMPLEMENT_FEATURE = 3,
PCMK_LSB_INSUFFICIENT_PRIV = 4,
diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h
index fa87599..4774d9a 100644
--- a/include/crm/stonith-ng.h
+++ b/include/crm/stonith-ng.h
@@ -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.
*
@@ -32,11 +32,6 @@ extern "C" {
# include <stdint.h> // uint32_t
# include <time.h> // time_t
-# define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect"
-# define T_STONITH_NOTIFY_FENCE "st_notify_fence"
-# define T_STONITH_NOTIFY_HISTORY "st_notify_history"
-# define T_STONITH_NOTIFY_HISTORY_SYNCED "st_notify_history_synced"
-
/* *INDENT-OFF* */
enum stonith_state {
stonith_connected_command,
@@ -192,21 +187,21 @@ typedef struct stonith_api_operations_s
/*!
* \brief Register a fence device with the local fencer
*
- * \param[in,out] st Fencer connection to use
- * \param[in] options Group of enum stonith_call_options
- * \param[in] id ID of fence device to register
- * \param[in] namespace Type of fence agent to search for ("redhat"
- * or "stonith-ng" for RHCS-style, "internal" for
- * Pacemaker-internal devices, "heartbeat" for
- * LHA-style, or "any" or NULL for any)
- * \param[in] agent Name of fence agent for device
- * \param[in] params Fence agent parameters for device
+ * \param[in,out] st Fencer connection to use
+ * \param[in] options Group of enum stonith_call_options
+ * \param[in] id ID of fence device to register
+ * \param[in] namespace_s Type of fence agent to search for ("redhat"
+ * or "stonith-ng" for RHCS-style, "internal"
+ * for Pacemaker-internal devices, "heartbeat"
+ * for LHA-style, or "any" or NULL for any)
+ * \param[in] agent Name of fence agent for device
+ * \param[in] params Fence agent parameters for device
*
* \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
* on success, otherwise a negative legacy Pacemaker return code
*/
int (*register_device)(stonith_t *st, int options, const char *id,
- const char *namespace, const char *agent,
+ const char *namespace_s, const char *agent,
const stonith_key_value_t *params);
/*!
@@ -245,7 +240,7 @@ typedef struct stonith_api_operations_s
* \param[in] call_options Group of enum stonith_call_options
* (currently ignored)
* \param[in] agent Fence agent to query
- * \param[in] namespace Type of fence agent to search for ("redhat"
+ * \param[in] namespace_s Type of fence agent to search for ("redhat"
* or "stonith-ng" for RHCS-style, "internal"
* for Pacemaker-internal devices, "heartbeat"
* for LHA-style, or "any" or NULL for any)
@@ -256,7 +251,7 @@ typedef struct stonith_api_operations_s
* \note The caller is responsible for freeing *output using free().
*/
int (*metadata)(stonith_t *stonith, int call_options, const char *agent,
- const char *namespace, char **output, int timeout_sec);
+ const char *namespace_s, char **output, int timeout_sec);
/*!
* \brief Retrieve a list of installed fence agents
@@ -264,7 +259,7 @@ typedef struct stonith_api_operations_s
* \param[in,out] stonith Fencer connection to use
* \param[in] call_options Group of enum stonith_call_options
* (currently ignored)
- * \param[in] namespace Type of fence agents to list ("redhat"
+ * \param[in] namespace_s Type of fence agents to list ("redhat"
* or "stonith-ng" for RHCS-style, "internal" for
* Pacemaker-internal devices, "heartbeat" for
* LHA-style, or "any" or NULL for all)
@@ -277,7 +272,7 @@ typedef struct stonith_api_operations_s
* stonith_key_value_freeall().
*/
int (*list_agents)(stonith_t *stonith, int call_options,
- const char *namespace, stonith_key_value_t **devices,
+ const char *namespace_s, stonith_key_value_t **devices,
int timeout);
/*!
@@ -580,6 +575,8 @@ const char *stonith_op_state_str(enum op_state state);
* queried without mainloop or the caller understanding the full API
*
* At least one of nodeid and uname are required
+ *
+ * NOTE: DLM uses both of these
*/
int stonith_api_kick(uint32_t nodeid, const char *uname, int timeout, bool off);
time_t stonith_api_time(uint32_t nodeid, const char *uname, bool in_progress);
@@ -695,6 +692,18 @@ const char *stonith_action_str(const char *action);
* we have to duplicate these declarations where they're implemented.
*/
+//! \deprecated Do not use
+#define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect"
+
+//! \deprecated Do not use
+#define T_STONITH_NOTIFY_FENCE "st_notify_fence"
+
+//! \deprecated Do not use
+#define T_STONITH_NOTIFY_HISTORY "st_notify_history"
+
+//! \deprecated Do not use
+#define T_STONITH_NOTIFY_HISTORY_SYNCED "st_notify_history_synced"
+
//! \deprecated Use stonith_get_namespace() instead
const char *get_stonith_provider(const char *agent, const char *provider);
diff --git a/include/crm_config.h.in b/include/crm_config.h.in
index a229226..a7ec62e 100644
--- a/include/crm_config.h.in
+++ b/include/crm_config.h.in
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2021 the Pacemaker project contributors
+ * Copyright 2006-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -43,6 +43,7 @@
/* Where to keep scheduler outputs */
#undef PE_STATE_DIR
+// NOTE: sbd (as of at least 1.5.2) uses this
/* Location to store core files produced by Pacemaker daemons */
#undef CRM_CORE_DIR
@@ -63,6 +64,7 @@
/* Set of enabled features */
#undef CRM_FEATURES
+// NOTE: sbd (as of at least 1.5.2) uses this
/* Support the Corosync messaging and membership layer */
#undef SUPPORT_COROSYNC
@@ -70,6 +72,7 @@
#undef SUPPORT_SYSTEMD
/* Support upstart based system services */
+//! \deprecated Do not use (always treat as 0)
#undef SUPPORT_UPSTART
#endif /* CRM_CONFIG__H */
diff --git a/include/crm_internal.h b/include/crm_internal.h
index 71a0f7e..590f88c 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2023 the Pacemaker project contributors
+ * Copyright 2006-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -35,12 +35,17 @@
#define PCMK_ALLOW_DEPRECATED 0
# include <crm/lrmd.h>
+# include <crm/cluster/internal.h>
# include <crm/common/logging.h>
# include <crm/common/logging_internal.h>
# include <crm/common/ipc_internal.h>
# include <crm/common/options_internal.h>
# include <crm/common/output_internal.h>
+# include <crm/common/scheduler_internal.h>
+# include <crm/common/schemas_internal.h>
# include <crm/common/xml_internal.h>
+# include <crm/common/xml_io_internal.h>
+# include <crm/common/xml_names_internal.h>
# include <crm/common/internal.h>
# include <locale.h>
# include <gettext.h>
@@ -55,46 +60,6 @@
/*
- * XML attribute names used only by internal code
- */
-
-#define PCMK__XA_ATTR_DAMPENING "attr_dampening"
-#define PCMK__XA_ATTR_FORCE "attrd_is_force_write"
-#define PCMK__XA_ATTR_INTERVAL "attr_clear_interval"
-#define PCMK__XA_ATTR_IS_PRIVATE "attr_is_private"
-#define PCMK__XA_ATTR_IS_REMOTE "attr_is_remote"
-#define PCMK__XA_ATTR_NAME "attr_name"
-#define PCMK__XA_ATTR_NODE_ID "attr_host_id"
-#define PCMK__XA_ATTR_NODE_NAME "attr_host"
-#define PCMK__XA_ATTR_OPERATION "attr_clear_operation"
-#define PCMK__XA_ATTR_PATTERN "attr_regex"
-#define PCMK__XA_ATTR_RESOURCE "attr_resource"
-#define PCMK__XA_ATTR_SECTION "attr_section"
-#define PCMK__XA_ATTR_SET "attr_set"
-#define PCMK__XA_ATTR_SET_TYPE "attr_set_type"
-#define PCMK__XA_ATTR_SYNC_POINT "attr_sync_point"
-#define PCMK__XA_ATTR_USER "attr_user"
-#define PCMK__XA_ATTR_UUID "attr_key"
-#define PCMK__XA_ATTR_VALUE "attr_value"
-#define PCMK__XA_ATTR_VERSION "attr_version"
-#define PCMK__XA_ATTR_WRITER "attr_writer"
-#define PCMK__XA_CONFIG_ERRORS "config-errors"
-#define PCMK__XA_CONFIG_WARNINGS "config-warnings"
-#define PCMK__XA_CONFIRM "confirm"
-#define PCMK__XA_CRMD "crmd"
-#define PCMK__XA_EXPECTED "expected"
-#define PCMK__XA_GRAPH_ERRORS "graph-errors"
-#define PCMK__XA_GRAPH_WARNINGS "graph-warnings"
-#define PCMK__XA_IN_CCM "in_ccm"
-#define PCMK__XA_JOIN "join"
-#define PCMK__XA_MODE "mode"
-#define PCMK__XA_NODE_START_STATE "node_start_state"
-#define PCMK__XA_TASK "task"
-#define PCMK__XA_UPTIME "uptime"
-#define PCMK__XA_CONN_HOST "connection_host"
-
-
-/*
* IPC service names that are only used internally
*/
@@ -113,7 +78,6 @@
#define PCMK__ATTRD_CMD_QUERY "query"
#define PCMK__ATTRD_CMD_REFRESH "refresh"
#define PCMK__ATTRD_CMD_FLUSH "flush"
-#define PCMK__ATTRD_CMD_SYNC "sync"
#define PCMK__ATTRD_CMD_SYNC_RESPONSE "sync-response"
#define PCMK__ATTRD_CMD_CLEAR_FAILURE "clear-failure"
#define PCMK__ATTRD_CMD_CONFIRM "confirm"
diff --git a/include/pacemaker-internal.h b/include/pacemaker-internal.h
index 9e6ff21..7d84ed6 100644
--- a/include/pacemaker-internal.h
+++ b/include/pacemaker-internal.h
@@ -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,6 +14,7 @@
# include <pcmki/pcmki_agents.h>
# include <pcmki/pcmki_cluster_queries.h>
# include <pcmki/pcmki_fence.h>
+# include <pcmki/pcmki_options.h>
# include <pcmki/pcmki_output.h>
# include <pcmki/pcmki_resource.h>
# include <pcmki/pcmki_result_code.h>
@@ -21,6 +22,8 @@
# include <pcmki/pcmki_scheduler.h>
# include <pcmki/pcmki_simulate.h>
# include <pcmki/pcmki_status.h>
+# include <pcmki/pcmki_ticket.h>
# include <pcmki/pcmki_transition.h>
+# include <pcmki/pcmki_verify.h>
#endif
diff --git a/include/pacemaker.h b/include/pacemaker.h
index ffa99ff..75d1b1f 100644
--- a/include/pacemaker.h
+++ b/include/pacemaker.h
@@ -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.
*
@@ -196,19 +196,31 @@ int pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
unsigned int message_timeout_ms);
/*!
+ * \brief Remove a resource
+ *
+ * \param[in,out] xml Destination for the result, as an XML tree
+ * \param[in] rsc_id Resource to remove
+ * \param[in] rsc_type Type of the resource ("primitive", "group", etc.)
+ *
+ * \return Standard Pacemaker return code
+ * \note This function will return \p pcmk_rc_ok if \p rsc_id doesn't exist
+ * or if \p rsc_type is incorrect for \p rsc_id (deleting something
+ * that doesn't exist always succeeds).
+ */
+int pcmk_resource_delete(xmlNodePtr *xml, const char *rsc_id, const char *rsc_type);
+
+/*!
* \brief Calculate and output resource operation digests
*
* \param[out] xml Where to store XML with result
* \param[in,out] rsc Resource to calculate digests for
* \param[in] node Node whose operation history should be used
* \param[in] overrides Hash table of configuration parameters to override
- * \param[in] scheduler Scheduler data (with status)
*
* \return Standard Pacemaker return code
*/
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);
/*!
* \brief Simulate a cluster's response to events
@@ -245,6 +257,22 @@ int pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
const char *dot_file);
/*!
+ * \brief Verify that a CIB is error-free or output errors and warnings
+ *
+ * This high-level function essentially implements crm_verify(8). It operates
+ * on an input CIB file, which can be inputted through one of several ways. It
+ * writes out XML-formatted output.
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] cib_source Source of the CIB:
+ * NULL -> use live cib, "-" -> stdin
+ * "<..." -> xml str, otherwise -> xml file name
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_verify(xmlNodePtr *xml, const char *cib_source);
+
+/*!
* \brief Get nodes list
*
* \param[in,out] xml The destination for the result, as an XML tree
@@ -376,6 +404,144 @@ int pcmk_list_providers(xmlNodePtr *xml, const char *agent_spec);
*/
int pcmk_list_standards(xmlNodePtr *xml);
+/*!
+ * \brief List all available cluster options
+ *
+ * These are options that affect the entire cluster.
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] all If \c true, include advanced and deprecated options
+ * (currently always treated as true)
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_list_cluster_options(xmlNode **xml, bool all);
+
+/*!
+ * \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] xml The destination for the result, as an XML tree
+ * \param[in] all If \c true, include advanced and deprecated options
+ * (currently always treated as true)
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_list_fencing_params(xmlNode **xml, bool all);
+
+/*!
+ * \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(xmlNode **xml, bool all);
+
+/*!
+ * \brief Return constraints that apply to the given ticket
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to find constraint for, or \c NULL for
+ * all ticket constraints
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_ticket_constraints(xmlNodePtr *xml, const char *ticket_id);
+
+
+/*!
+ * \brief Delete a ticket's state from the local cluster site
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to delete
+ * \param[in] force If \c true, delete the ticket even if it has
+ * been granted
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_ticket_delete(xmlNodePtr *xml, const char *ticket_id, bool force);
+
+/*!
+ * \brief Return the value of a ticket's attribute
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to find attribute value for
+ * \param[in] attr_name Attribute's name to find value for
+ * \param[in] attr_default If either the ticket or the attribute do not
+ * exist, use this as the value in \p xml
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_ticket_get_attr(xmlNodePtr *xml, const char *ticket_id,
+ const char *attr_name, const char *attr_default);
+
+/*!
+ * \brief Return information about the given ticket
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to find info value for, or \c NULL for
+ * all tickets
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_ticket_info(xmlNodePtr *xml, const char *ticket_id);
+
+/*!
+ * \brief Remove the given attribute(s) from a ticket
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to remove attributes from
+ * \param[in] attr_delete A list of attribute names
+ * \param[in] force Attempting to remove the granted attribute of
+ * \p ticket_id will cause this function to return
+ * \c EACCES unless \p force is set to \c true
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk_ticket_remove_attr(xmlNodePtr *xml, const char *ticket_id, GList *attr_delete,
+ bool force);
+
+/*!
+ * \brief Set the given attribute(s) on a ticket
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to set attributes on
+ * \param[in] attr_set A hash table of attributes, where keys are the
+ * attribute names and the values are the attribute
+ * values
+ * \param[in] force Attempting to change the granted status of
+ * \p ticket_id will cause this function to return
+ * \c EACCES unless \p force is set to \c true
+ *
+ * \return Standard Pacemaker return code
+ *
+ * \note If no \p ticket_id attribute exists but \p attr_set is non-NULL, the
+ * ticket will be created with the given attributes.
+ */
+int pcmk_ticket_set_attr(xmlNodePtr *xml, const char *ticket_id, GHashTable *attr_set,
+ bool force);
+
+/*!
+ * \brief Return a ticket's state XML
+ *
+ * \param[in,out] xml The destination for the result, as an XML tree
+ * \param[in] ticket_id Ticket to find state for, or \c NULL for all
+ * tickets
+ *
+ * \return Standard Pacemaker return code
+ *
+ * \note If \p ticket_id is not \c NULL and more than one ticket exists with
+ * that ID, this function returns \c pcmk_rc_duplicate_id.
+ */
+int pcmk_ticket_state(xmlNodePtr *xml, const char *ticket_id);
+
#ifdef BUILD_PUBLIC_LIBPACEMAKER
/*!
diff --git a/include/pcmki/pcmki_options.h b/include/pcmki/pcmki_options.h
new file mode 100644
index 0000000..6d6a1aa
--- /dev/null
+++ b/include/pcmki/pcmki_options.h
@@ -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 Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#ifndef PCMK__PCMKI_PCMKI_OPTIONS__H
+#define PCMK__PCMKI_PCMKI_OPTIONS__H
+
+#include <crm/common/output_internal.h>
+
+int pcmk__list_cluster_options(pcmk__output_t *out, bool all);
+int pcmk__list_fencing_params(pcmk__output_t *out, bool all);
+int pcmk__list_primitive_meta(pcmk__output_t *out, bool all);
+
+#endif // PCMK__PCMKI_PCMKI_OPTIONS__H
diff --git a/include/pcmki/pcmki_resource.h b/include/pcmki/pcmki_resource.h
index 442bb1f..973b896 100644
--- a/include/pcmki/pcmki_resource.h
+++ b/include/pcmki/pcmki_resource.h
@@ -11,9 +11,13 @@
#include <glib.h>
+#include <crm/cib/cib_types.h>
#include <crm/common/scheduler.h>
#include <crm/common/output_internal.h>
+int pcmk__resource_delete(cib_t *cib, uint32_t cib_opts, const char *rsc_id,
+ const char *rsc_type);
+
int pcmk__resource_digests(pcmk__output_t *out, pcmk_resource_t *rsc,
const pcmk_node_t *node, GHashTable *overrides);
diff --git a/include/pcmki/pcmki_ticket.h b/include/pcmki/pcmki_ticket.h
new file mode 100644
index 0000000..a4606fc
--- /dev/null
+++ b/include/pcmki/pcmki_ticket.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__PCMKI_PCMKI_TICKET__H
+# define PCMK__PCMKI_PCMKI_TICKET__H
+
+#include <crm/common/output_internal.h>
+
+#include <crm/cib/cib_types.h>
+
+/*!
+ * \internal
+ * \brief Return the state XML for a given ticket
+ *
+ * \param[in] cib Open CIB connection
+ * \param[in] ticket_id Ticket to get state for, or \c NULL for all tickets
+ * \param[out] state Where to store the result XML
+ *
+ * \return Standard Pacemaker return code
+ *
+ * \note If \p ticket_id is not \c NULL and more than one ticket exists with
+ * that ID, this function returns \c pcmk_rc_duplicate_id.
+ */
+int pcmk__get_ticket_state(cib_t *cib, const char *ticket_id, xmlNode **state);
+
+/*!
+ * \internal
+ * \brief Display the constraints that apply to a given ticket
+ *
+ * \param[in,out] out Output object
+ * \param[in] cib Open CIB connection
+ * \param[in] ticket_id Ticket to find constraints for,
+ * or \c NULL for all ticket constraints
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__ticket_constraints(pcmk__output_t *out, cib_t *cib, const char *ticket_id);
+
+/*!
+ * \internal
+ * \brief Delete a ticket's state from the local cluster site
+ *
+ * \param[in,out] out Output object
+ * \param[in] cib Open CIB connection
+ * \param[in] scheduler Scheduler data
+ * \param[in] ticket_id Ticket to delete
+ * \param[in] force If \c true, delete the ticket even if it has
+ * been granted
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__ticket_delete(pcmk__output_t *out, cib_t *cib, pcmk_scheduler_t *scheduler,
+ const char *ticket_id, bool force);
+
+/*!
+ * \internal
+ * \brief Return the value of a ticket's attribute
+ *
+ * \param[in,out] out Output object
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] ticket_id Ticket to find attribute value for
+ * \param[in] attr_name Attribute's name to find value for
+ * \param[in] attr_default If either the ticket or the attribute do not
+ * exist, use this as the value in the output
+ *
+ * \return Standard Pacemaker return code
+ */
+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);
+
+/*!
+ * \brief Return information about the given ticket
+ *
+ * \param[in,out] out Output object
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] ticket_id Ticket to display info for, or \c NULL for
+ * all tickets
+ * \param[in] details If true (and \p out is not an XML format
+ * object), output any additional attributes
+ * set on a ticket beyond the basics
+ * \param[in] raw If true (and \p out is not an XML format
+ * object), simply list the IDs of all tickets.
+ * This does not make a lot of sense if
+ * \p ticket_id is not NULL, but that will not
+ * raise an error.
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__ticket_info(pcmk__output_t *out, pcmk_scheduler_t *scheduler,
+ const char *ticket_id, bool details, bool raw);
+
+/*!
+ * \brief Remove the given attribute(s) from a ticket
+ *
+ * \param[in,out] out Output object
+ * \param[in] cib Open CIB connection
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] ticket_id Ticket to remove attributes from
+ * \param[in] attr_delete A list of attribute names
+ * \param[in] force Attempting to remove the granted attribute of
+ * \p ticket_id will cause this function to return
+ * \c EACCES unless \p force is set to \c true
+ *
+ * \return Standard Pacemaker return code
+ */
+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);
+
+/*!
+ * \brief Set the given attribute(s) on a ticket
+ *
+ * \param[in,out] out Output object
+ * \param[in] cib Open CIB connection
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] ticket_id Ticket to set attributes on
+ * \param[in] attr_set A hash table of attributes, where keys are the
+ * attribute names and the values are the attribute
+ * values
+ * \param[in] force Attempting to change the granted status of
+ * \p ticket_id will cause this function to return
+ * \c EACCES unless \p force is set to \c true
+ *
+ * \return Standard Pacemaker return code
+ *
+ * \note If no \p ticket_id attribute exists but \p attr_set is non-NULL, the
+ * ticket will be created with the given attributes.
+ */
+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);
+
+/*!
+ * \internal
+ * \brief Return a ticket's state XML
+ *
+ * \param[in,out] out Output object
+ * \param[in] cib Open CIB connection
+ * \param[in] ticket_id Ticket to find constraints for,
+ * or \c NULL for all ticket constraints
+ *
+ * \return Standard Pacemaker return code
+ *
+ * \note If \p ticket_id is not \c NULL and more than one ticket exists with
+ * that ID, this function returns \c pcmk_rc_duplicate_id.
+ */
+int pcmk__ticket_state(pcmk__output_t *out, cib_t *cib, const char *ticket_id);
+
+#endif
diff --git a/include/pcmki/pcmki_transition.h b/include/pcmki/pcmki_transition.h
index 93237ed..73ab3aa 100644
--- a/include/pcmki/pcmki_transition.h
+++ b/include/pcmki/pcmki_transition.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -12,7 +12,6 @@
# include <glib.h>
# include <crm/crm.h>
-# include <crm/msg_xml.h>
# include <crm/common/xml.h>
# include <crm/lrmd_events.h> // lrmd_event_data_t
diff --git a/include/pcmki/pcmki_verify.h b/include/pcmki/pcmki_verify.h
new file mode 100644
index 0000000..28f20cd
--- /dev/null
+++ b/include/pcmki/pcmki_verify.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef PCMK__PCMKI_PCMKI_VERIFY__H
+# define PCMK__PCMKI_PCMKI_VERIFY__H
+
+#include <crm/common/output_internal.h>
+#include <crm/common/scheduler.h>
+#include <libxml/tree.h>
+
+/*!
+ * \internal
+ * \brief Parse a CIB file
+ *
+ * This function parses a CIB file into a CIB object
+ *
+ * \param[in] out Output to use for logging and printing results
+ * \param[in] cib_source Source of the CIB:
+ * NULL -> use live cib, "-" -> stdin
+ * "<..." -> xml str, otherwise -> xml file name
+ * \param[in,out] cib_object The resulting, parsed CIB object
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__parse_cib(pcmk__output_t *out, const char *cib_source, xmlNodePtr *cib_object);
+
+/*!
+ * \internal
+ * \brief Verify that a CIB is error-free or output errors and warnings
+ *
+ * This high-level function essentially implements crm_verify(8). It operates
+ * on an input CIB file, which can be inputted through one of several ways. It
+ * can either write out XML-formatted output or plaintext output.
+ *
+ * \param[in,out] scheduler Scheduler data
+ * \param[in] out Output to use for logging and printing results
+ * \param[in] cib_object The parsed CIB object
+ *
+ * \return Standard Pacemaker return code
+ */
+int pcmk__verify(pcmk_scheduler_t *scheduler, pcmk__output_t *out, xmlNode *cib_object);
+
+#endif
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)
diff --git a/m4/version.m4 b/m4/version.m4
index f469bba..0e9f1e4 100644
--- a/m4/version.m4
+++ b/m4/version.m4
@@ -1,2 +1,2 @@
-m4_define([VERSION_NUMBER], [2.1.7])
+m4_define([VERSION_NUMBER], [2.1.8])
m4_define([PCMK_URL], [https://ClusterLabs.org/pacemaker/])
diff --git a/mk/tap.mk b/mk/tap.mk
index fd6d4e2..e06f9a8 100644
--- a/mk/tap.mk
+++ b/mk/tap.mk
@@ -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.
#
@@ -7,15 +7,18 @@
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
-AM_TESTS_ENVIRONMENT= \
- G_DEBUG=gc-friendly \
- MALLOC_CHECK_=2 \
- MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
+AM_TESTS_ENVIRONMENT = G_DEBUG=gc-friendly
+AM_TESTS_ENVIRONMENT += MALLOC_CHECK_=2
+AM_TESTS_ENVIRONMENT += MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256))
+AM_TESTS_ENVIRONMENT += PCMK_CTS_CLI_DIR=$(top_srcdir)/cts/cli
+AM_TESTS_ENVIRONMENT += PCMK_schema_directory=$(top_builddir)/xml
+
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tests/tap-driver.sh
LOG_COMPILER = $(top_srcdir)/tests/tap-test
CLEANFILES = *.log *.trs
-WRAPPED = calloc \
+WRAPPED = abort \
+ calloc \
endgrent \
fopen \
getenv \
@@ -23,10 +26,10 @@ WRAPPED = calloc \
getgrent \
getpwnam_r \
readlink \
+ realloc \
setenv \
setgrent \
strdup \
- uname \
unsetenv
if WRAPPABLE_FOPEN64
diff --git a/mk/unittest.mk b/mk/unittest.mk
index ea397f2..f563ea3 100644
--- a/mk/unittest.mk
+++ b/mk/unittest.mk
@@ -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.
#
@@ -12,8 +12,17 @@ AM_CPPFLAGS = -I$(top_builddir)/include \
-I$(top_srcdir)/lib/common
AM_CFLAGS = -DPCMK__UNIT_TESTING
+# Add -fno-builtin and -fno-inline to allow mocking realloc.
+AM_CFLAGS += -fno-builtin
+AM_CFLAGS += -fno-inline
AM_LDFLAGS = $(LDFLAGS_WRAP)
-LDADD = $(top_builddir)/lib/common/libcrmcommon_test.la \
- -lcmocka
+LDADD = $(top_builddir)/lib/common/libcrmcommon_test.la
+if BUILD_COVERAGE
+LDADD += -lgcov
+endif
+LDADD += -lcmocka
+# When -fno-builtin is used, -lm also needs to be added. See the comments in
+# lib/common/Makefile.am for libcrmcommon_test_la_CFLAGS.
+LDADD += -lm
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 93249f8..d79554c 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -1,5 +1,5 @@
#
-# Copyright 2003-2022 the Pacemaker project contributors
+# Copyright 2003-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,7 +11,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Pacemaker 2\n"
"Report-Msgid-Bugs-To: developers@clusterlabs.org\n"
-"POT-Creation-Date: 2023-12-05 15:13+0800\n"
+"POT-Creation-Date: 2024-05-13 16:10-0500\n"
"PO-Revision-Date: 2021-11-08 11:04+0800\n"
"Last-Translator: Vivi <developers@clusterlabs.org>\n"
"Language-Team: CHINESE <wangluwei@uniontech.org>\n"
@@ -20,194 +20,161 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: daemons/controld/controld_control.c:526
+#: daemons/fenced/pacemaker-fenced.c:498
+msgid "Instance attributes available for all \"stonith\"-class resources"
+msgstr " 可用于所有stonith类资源的实例属性"
+
+#: daemons/fenced/pacemaker-fenced.c:500
+msgid ""
+"Instance attributes available for all \"stonith\"-class resources and used "
+"by Pacemaker's fence daemon, formerly known as stonithd"
+msgstr ""
+" 可用于所有stonith类资源的实例属性,并由Pacemaker的fence守护程序使用(以前称"
+"为stonithd)"
+
+#: daemons/fenced/pacemaker-fenced.c:511
+msgid "Deprecated (will be removed in a future release)"
+msgstr "已弃用(将在未来版本中删除)"
+
+#: daemons/fenced/pacemaker-fenced.c:514
+msgid "Intended for use in regression testing only"
+msgstr "仅适用于回归测试"
+
+#: daemons/fenced/pacemaker-fenced.c:517
+msgid "Send logs to the additional named logfile"
+msgstr "将日志发送到其他命名日志文件"
+
+#: lib/common/options.c:57
msgid "Pacemaker version on cluster node elected Designated Controller (DC)"
msgstr "集群选定的控制器节点(DC)的 Pacemaker 版本"
-#: daemons/controld/controld_control.c:527
+#: lib/common/options.c:59
+#, fuzzy
msgid ""
-"Includes a hash which identifies the exact changeset the code was built "
-"from. Used for diagnostic purposes."
-msgstr "它包含一个标识所构建代码变更版本的哈希值,其可用于诊断。"
+"Includes a hash which identifies the exact revision the code was built from. "
+"Used for diagnostic purposes."
+msgstr "它包含一个标识所构建代码修订版本的哈希值. 其可用于诊断."
-#: daemons/controld/controld_control.c:532
-msgid "The messaging stack on which Pacemaker is currently running"
-msgstr "Pacemaker 正在使用的消息传输引擎"
+#: lib/common/options.c:66
+#, fuzzy
+msgid "The messaging layer on which Pacemaker is currently running"
+msgstr "Pacemaker 当前运行的消息传递层"
-#: daemons/controld/controld_control.c:533
+#: lib/common/options.c:67
msgid "Used for informational and diagnostic purposes."
-msgstr "用于提供信息和诊断。"
+msgstr "用于提供信息和诊断."
-#: daemons/controld/controld_control.c:537
+#: lib/common/options.c:73
msgid "An arbitrary name for the cluster"
msgstr "任意的集群名称"
-#: daemons/controld/controld_control.c:538
+#: lib/common/options.c:74
msgid ""
"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."
msgstr ""
-"该可选值主要是为了方便用户管理使用,也可以在pacemaker 配置规则中通过 "
-"#cluster-name 节点属性配置使用,也可以通过高级工具和资源代理使用。"
+"该可选值主要是为了方便用户根据管理的需要使用, 可以通过 #cluster-name 节点属性"
+"在 Pacemaker 配置规则中使用, 以及被更高级的工具和资源代理使用."
-#: daemons/controld/controld_control.c:546
+#: lib/common/options.c:83
msgid "How long to wait for a response from other nodes during start-up"
msgstr "启动过程中等待其他节点响应的时间"
-#: daemons/controld/controld_control.c:547
+#: lib/common/options.c:84
msgid ""
"The optimal value will depend on the speed and load of your network and the "
"type of switches used."
-msgstr "其最佳值将取决于你的网络速度和负载以及所用交换机的类型。"
+msgstr "其最佳值将取决于您的网络速度和负载以及使用的交换机类型."
-#: daemons/controld/controld_control.c:552
-msgid ""
-"Zero disables polling, while positive values are an interval in "
-"seconds(unless other units are specified, for example \"5min\")"
-msgstr ""
-"设置为0将禁用轮询,设置为正数将是以秒为单位的时间间隔(除非使用了其他单位,比"
-"如\"5min\"表示5分钟)"
-
-#: daemons/controld/controld_control.c:555
+#: lib/common/options.c:91
msgid ""
"Polling interval to recheck cluster state and evaluate rules with date "
"specifications"
-msgstr "重新检查集群状态并且评估具有日期规格的配置规则的轮询间隔"
+msgstr "重新检查集群状态及评估日期规范规则的轮询间隔"
-#: daemons/controld/controld_control.c:557
+#: lib/common/options.c:93
+#, fuzzy
msgid ""
"Pacemaker is primarily event-driven, and looks ahead to know when to recheck "
-"cluster state for failure timeouts 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."
+"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\")."
msgstr ""
-"Pacemaker 主要是通过事件驱动的,并能预期重新检查集群状态以评估大多数基于时间"
-"的规则以及过期的错误。然而无论如何,在集群经过该时间间隔的不活动状态后,它还"
-"将重新检查集群,以评估具有日期规格的规则,并为某些类型的调度程序缺陷提供故障"
-"保护。"
-
-#: daemons/controld/controld_control.c:566
-msgid "Maximum amount of system load that should be used by cluster nodes"
-msgstr "集群节点应该使用的最大系统负载量"
-
-#: daemons/controld/controld_control.c:567
-msgid ""
-"The cluster will slow down its recovery process when the amount of system "
-"resources used (currently CPU) approaches this limit"
-msgstr "当使用的系统资源量(当前为CPU)接近此限制时,集群将减慢其恢复过程"
-
-#: daemons/controld/controld_control.c:573
-msgid ""
-"Maximum number of jobs that can be scheduled per node (defaults to 2x cores)"
-msgstr "每个节点可以调度的最大作业数(默认为2x内核数)"
+"Pacemaker 主要是通过事件驱动的, 并会提前预测何时重新检查集群状态以评估大多数"
+"基于时间的规则以及 failure-timeout 配置, 然而无论如何, 经过指定的时间后如果没"
+"有活动, 它将重新检查集群, 以评估具有日期规范的规则, 并为某些类型的调度程序缺"
+"陷提供故障保护. 如果值为0, 将禁用轮询. 如果值为正数, 则设置以秒为单位的时间间"
+"隔, 除非指定了其它单位 (例如, \"5min\")."
-#: daemons/controld/controld_control.c:577
+#: lib/common/options.c:107
msgid "How a cluster node should react if notified of its own fencing"
msgstr "集群节点在收到针对自己的 fence 操作结果通知时应如何反应"
-#: daemons/controld/controld_control.c:578
+#: lib/common/options.c:108
+#, fuzzy
msgid ""
-"A cluster node may receive notification of its own fencing if fencing is "
-"misconfigured, or if fabric fencing is in use that doesn't cut cluster "
-"communication. Allowed values are \"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."
+"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."
msgstr ""
-"如果有错误的 fence 配置,或者在使用 fabric fence 机制 (并不会切断集群通信),"
-"则集群节点可能会收到针对自己的 fence 结果通知。允许的值为 \"stop\" 尝试立即停"
-"止 pacemaker 并保持停用状态,或者 \"panic\" 尝试立即重新启动本地节点,并在失败"
-"时返回执行stop。"
+"如果有错误的 fence 配置, 或者在使用 fabric fence 机制 (并不会切断集群通信), "
+"则集群节点可能会收到针对自己的 \"succeeded\" fence 结果通知. 使用 \"stop\" 尝"
+"试立即停止 pacemaker 并保持停止状态,或者使用 \"panic\" 尝试立即重新启动本地节"
+"点,如果失败则返回执行 stop."
-#: daemons/controld/controld_control.c:588
+#: lib/common/options.c:119
msgid ""
"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."
msgstr ""
-"如果集群在本项设置时间内没有作出决定则宣布选举失败。如果您需要调整该值,这可"
-"能代表存在某些缺陷。"
+"如果集群在本项设置时间内没有作出决定则宣布选举失败. 这可能表明当前存在错误, "
+"您需要调整该值."
-#: daemons/controld/controld_control.c:596
+#: lib/common/options.c:128
msgid ""
"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."
msgstr ""
-"如果在这段时间内关机仍未完成,则立即退出。如果您需要调整该值,这可能代表存在"
-"某些缺陷。"
+"如果在这段时间内关机仍未完成, pacemaker 将立即退出. 这可能表明当前存在错误, "
+"您需要调整该值."
-#: daemons/controld/controld_control.c:604
-#: daemons/controld/controld_control.c:611
+#: lib/common/options.c:138 lib/common/options.c:147
msgid ""
"If you need to adjust this value, it probably indicates the presence of a "
"bug."
-msgstr "如果您需要调整该值,这可能代表存在某些缺陷。"
+msgstr "这可能表明当前存在错误, 您需要调整该值."
-#: daemons/controld/controld_control.c:617
+#: lib/common/options.c:156
+#, fuzzy
msgid ""
-"*** Advanced Use Only *** Enabling this option will slow down cluster "
-"recovery under all conditions"
-msgstr "*** Advanced Use Only *** 启用此选项将在所有情况下减慢集群恢复的速度"
+"Enabling this option will slow down cluster recovery under all conditions"
+msgstr "启用此选项将在所有情况下减慢集群恢复的速度"
-#: daemons/controld/controld_control.c:619
+#: lib/common/options.c:158
msgid ""
"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."
msgstr ""
-"集群恢复将被推迟指定的时间间隔,以等待更多事件发生。如果您的配置对 ping 更新"
-"到达的顺序很敏感,这就很有用"
+"集群恢复将被推迟指定的时间间隔, 以等待更多事件发生. 如果您的配置对 ping 更新"
+"到达的顺序很敏感, 则可以使用此选项."
-#: daemons/controld/controld_control.c:626
-#, fuzzy
-msgid ""
-"How long before nodes can be assumed to be safely down when watchdog-based "
-"self-fencing via SBD is in use"
-msgstr ""
-"当基于 watchdog 的自我 fence 机制通过SBD 被执行时,我们可以假设节点安全关闭之"
-"前需要等待多长时间"
-
-#: daemons/controld/controld_control.c:628
-msgid ""
-"If this is set to a positive value, lost nodes are assumed to self-fence "
-"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."
-msgstr ""
-"如果设置为正值,则假定丢失的节点在这段时间内使用基于watchdog的SBD进行自我防"
-"护。这不需要明确配置fence资源,但可以配置一个fence_watchdog资源,以限制特定节"
-"点的使用。如果设置为0(默认值),集群将永远不会假定基于watchdog的自我防护。如"
-"果设置为负值,且如果`SBD_WATCHDOG_TIMEOUT`环境变量的本地值为正值,则集群将使"
-"用该值的两倍,否则将其视为0。警告:在使用基于watchdog的SBD的所有节点上,此超"
-"时必须大于`SBD_WATCGDOG_TIMEOUT`,如果本地值不是这样,或者SBD未运行,则"
-"Pacemaker将拒绝在任何节点上启动。如果设置为负值,则在使用SBD的所有节点上,"
-"`SBD_WATCHDOG_TIMEOUT`必须设置为相同的值,否则可能会发生数据损坏或丢失。"
-
-#: daemons/controld/controld_control.c:647
-msgid ""
-"How many times fencing can fail before it will no longer be immediately re-"
-"attempted on a target"
-msgstr "fence操作失败多少次会停止立即尝试"
-
-#: daemons/controld/controld_control.c:655 lib/pengine/common.c:40
+#: lib/common/options.c:168
msgid "What to do when the cluster does not have quorum"
-msgstr "当集群没有必需票数时该如何作"
+msgstr "当集群没有达到必需票数时该如何做"
-#: daemons/controld/controld_control.c:660 lib/pengine/common.c:74
+#: lib/common/options.c:175
msgid "Whether to lock resources to a cleanly shut down node"
msgstr "是否锁定资源到完全关闭的节点"
-#: daemons/controld/controld_control.c:661 lib/pengine/common.c:75
+#: lib/common/options.c:176
msgid ""
"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 "
@@ -216,730 +183,1053 @@ msgid ""
"Clone and bundle instances and the promoted role of promotable clones are "
"currently never locked, though support could be added in a future release."
msgstr ""
-"设置为true时,在完全关闭的节点上活动的资源将被“锁定”到该节点(不允许在其他地"
-"方运行),直到该节点重新加入后资源重新启动(或最长shutdown-lock-limit,如果已"
-"设置)。 Stonith资源和Pacemaker Remote连接永远不会被锁定。 克隆和捆绑实例以及"
-"可升级克隆的主角色目前从未锁定,尽管可以在将来的发行版中添加支持。"
+"设置为 true 时, 在完全关闭的节点上活动的资源将被 \"locked\" 到该节点 (不允许"
+"在其它方运行), 直到该节点重新加入后它们再次在该节点上启动 (最长为 shutdown-"
+"lock-limit,如果已设置). Stonith 资源和 Pacemaker Remote 连接永远不会被锁定. "
+"克隆和捆绑实例以及可提升克隆的提升角色目前不会被锁定, 尽管可能在未来的发行版"
+"中添加支持. "
-#: daemons/controld/controld_control.c:673 lib/pengine/common.c:87
+#: lib/common/options.c:189
msgid "Do not lock resources to a cleanly shut down node longer than this"
msgstr "资源会被锁定到完全关闭的节点的最长时间"
-#: daemons/controld/controld_control.c:675 lib/pengine/common.c:89
+#: lib/common/options.c:191
msgid ""
"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."
msgstr ""
-"如果shutdown-lock为true,并且将此选项设置为非零持续时间,则自从开始shutdown以"
-"来经过了这么长的时间后,shutdown锁将过期,即使该节点尚未重新加入。"
+"如果 shutdown-lock 为 true, 并且将此选项设置为非零时间间隔, 则自关闭操作执行"
+"经过此时间后,shutdown lock 将过期, 即使该节点尚未重新加入也是如此. "
+
+#: lib/common/options.c:200
+msgid "Enable Access Control Lists (ACLs) for the CIB"
+msgstr "为 CIB 启用访问控制列表 (ACL) "
+
+#: lib/common/options.c:207
+msgid "Whether resources can run on any node by default"
+msgstr "默认情况下资源是否可以在任何节点上运行"
+
+#: lib/common/options.c:214
+msgid ""
+"Whether the cluster should refrain from monitoring, starting, and stopping "
+"resources"
+msgstr "集群是否应避免监视, 启动和停止资源"
+
+#: lib/common/options.c:222
+msgid ""
+"Whether a start failure should prevent a resource from being recovered on "
+"the same node"
+msgstr "资源启动失败是否应阻止在同一节点上恢复该资源"
+
+#: lib/common/options.c:224
+msgid ""
+"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."
+msgstr ""
+"当为true, 如果资源启动失败, 集群将立即禁止节点启动该资源, 当为false, 集群将检"
+"查资源的失败次数是否超过了其 migration-threshold. "
+
+#: lib/common/options.c:232
+msgid "Whether the cluster should check for active resources during start-up"
+msgstr "集群是否在启动期间检查活动的资源"
+
+#: lib/common/options.c:242
+#, fuzzy
+msgid "Whether nodes may be fenced as part of recovery"
+msgstr "节点是否可以被 fence 作为集群恢复的一部分"
+
+#: lib/common/options.c:243
+msgid ""
+"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."
+msgstr ""
+"如果为 false, 则立即假定无响应的节点是无害的, 并且可以在其它位置恢复在其上活"
+"动的资源. 这可能会导致 \"split-brain\" 情况, 从而可能导致数据丢失和(或)服务不"
+"可用. "
+
+#: lib/common/options.c:253
+msgid ""
+"Action to send to fence device when a node needs to be fenced (\"poweroff\" "
+"is a deprecated alias for \"off\")"
+msgstr ""
+"当节点需要被 fence 时, 向 fence 设备发送的操作 (\"poweroff\" 作为 \"off\" 的"
+"别名已被弃用)"
+
+#: lib/common/options.c:261
+msgid ""
+"How long to wait for on, off, and reboot fence actions to complete by default"
+msgstr "默认情况下, 等待 on, off, 和 reboot fence 操作完成的时间"
+
+#: lib/common/options.c:269
+msgid "Whether watchdog integration is enabled"
+msgstr "是否启用 watchdog 集成设置"
+
+#: lib/common/options.c:270
+msgid ""
+"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."
+msgstr ""
+"集群会根据是否检测到 SBD 正在使用来自动设置此值. 用户配置的值将被忽略. 如果使"
+"用了无盘 SBD 并且 `stonith-watchdog-timeout` 不为零, 则值 `true` 才有实际意"
+"义. 在这种情况下, 如果需要fence, 将通过 SBD 执行基于 watchdog 的自我 fence, "
+"而不需要明确配置 fence 资源."
+
+#: lib/common/options.c:291
+#, fuzzy
+msgid ""
+"How long before nodes can be assumed to be safely down when watchdog-based "
+"self-fencing via SBD is in use"
+msgstr ""
+"当基于 watchdog 的自我 fence 机制通过SBD 被执行时, 节点被认为安全下线的等待时"
+"间有多长"
+
+#: lib/common/options.c:293
+#, fuzzy
+msgid ""
+"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."
+msgstr ""
+"如果设为正值, 丢失的节点将在设定的时间内被认定使用基于 watchdog 的 SBD 完成自"
+"我 fence. 这不需要明确配置一个 fence 资源, 但可以配置一个 fence_watchdog 资源"
+"来限制对特定节点使用. 如果设为0 (默认值), 集群将永远不会认定节点使用基于 "
+"watchdog 的自我 fence. 如果设为负值, 集群将使用本地 `SBD_WATCHDOG_TIMEOUT` 环"
+"境变量的两倍值(如果该值为正), 否则会将该值视为0. 警告: 在所有使用基于 "
+"watchdog 的 SBD 的节点上, 此超时值需大于 `SBD_WATCHDOG_TIMEOUT` 的值, 否则 "
+"Pacemaker 不会在任何不符合此条件的节点上启动, 也不会在任何未启用 SBD 的节点上"
+"启动. 当设为负值时所有使用 SBD 的节点上 `SBD_WATCHDOG_TIMEOUT` 的值必须设置为"
+"相同的值, 否则可能导致数据损坏或丢失."
+
+#: lib/common/options.c:313
+msgid ""
+"How many times fencing can fail before it will no longer be immediately re-"
+"attempted on a target"
+msgstr "fence 操作失败多少次会停止立即尝试"
+
+#: lib/common/options.c:321
+msgid "Allow performing fencing operations in parallel"
+msgstr "允许并行执行 fencing 操作"
+
+#: lib/common/options.c:328
+#, fuzzy
+msgid "Whether to fence unseen nodes at start-up"
+msgstr "*** 仅高级使用 *** 是否在启动时fence不可见节点"
+
+#: lib/common/options.c:329
+#, fuzzy
+msgid ""
+"Setting this to false may lead to a \"split-brain\" situation, potentially "
+"leading to data loss and/or service unavailability."
+msgstr ""
+"将此设置为 false 可能会导致 \"split-brain\" 的情况,可能导致数据丢失和(或)服"
+"务不可用。"
+
+#: lib/common/options.c:336
+msgid ""
+"Apply fencing delay targeting the lost nodes with the highest total resource "
+"priority"
+msgstr "针对具有最高总资源优先级的丢失节点应用fencing延迟"
+
+#: lib/common/options.c:338
+msgid ""
+"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."
+msgstr ""
+"如果我们所在的集群分区并不拥有大多数集群节点,则针对丢失节点的fence操作应用指"
+"定的延迟,这样更重要的节点就能够赢得fence竞赛。这对于双节点集群在split-brain"
+"状况下尤其有意义。如果基本优先级不为0,在计算时主资源实例获得基本优先级+1。任"
+"何对于相应的 fence 资源由 pcmk_delay_base/max 配置所引入的静态/随机延迟会被添"
+"加到此延迟。为了安全, 这个延迟应该明显大于 pcmk_delay_base/max 的最大设置值,"
+"例如两倍。默认情况下,优先级fencing延迟已禁用。"
-#: daemons/controld/controld_control.c:683 lib/pengine/common.c:164
+#: lib/common/options.c:355
msgid ""
"How long to wait for a node that has joined the cluster to join the "
"controller process group"
-msgstr ""
-"等待已加入集群的节点加入控制器进程组的时间"
+msgstr "等待已加入集群的节点加入控制器进程组的时间"
-#: daemons/controld/controld_control.c:685 lib/pengine/common.c:166
+#: lib/common/options.c:357
msgid ""
"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."
msgstr ""
-"如果节点加入集群后在此时间内不加入控制器进程组,Fence该节点,以便群集继续管理资源。"
-"值为0表示永远不 fence 待定节点。将值设置为2h表示2小时后 fence 待定节点。"
+"如果节点加入集群后在此时间内不加入控制器进程组,Fence该节点,以便群集继续管理"
+"资源。值为0表示永远不 fence 待定节点。将值设置为2h表示2小时后 fence 待定节"
+"点。"
+
+#: lib/common/options.c:367
+msgid "Maximum time for node-to-node communication"
+msgstr "最大节点间通信时间"
+
+#: lib/common/options.c:368
+msgid ""
+"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."
+msgstr ""
+"如果一个操作未在该时间内(并且考虑操作本身的超时时长)从执行该操作的节点获得"
+"响应,则会被选为指定控制器(DC)的节点认定为失败。\"正确\" 值将取决于速度和您"
+"的网络和集群节点的负载。"
+
+#: lib/common/options.c:380
+msgid "Maximum amount of system load that should be used by cluster nodes"
+msgstr "集群节点应该使用的最大系统负载量"
+
+#: lib/common/options.c:382
+msgid ""
+"The cluster will slow down its recovery process when the amount of system "
+"resources used (currently CPU) approaches this limit"
+msgstr "当使用的系统资源量(当前指 CPU)接近此限制时, 集群将减慢其恢复过程"
+
+#: lib/common/options.c:389
+msgid ""
+"Maximum number of jobs that can be scheduled per node (defaults to 2x cores)"
+msgstr "每个节点可以调度的最大作业数(默认为2x内核数)"
+
+#: lib/common/options.c:397
+#, fuzzy
+msgid ""
+"Maximum number of jobs that the cluster may execute in parallel across all "
+"nodes"
+msgstr "集群可以在所有节点上并发执行的最大作业数"
+
+#: lib/common/options.c:399
+msgid ""
+"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."
+msgstr ""
+"\"正确\" 值将取决于速度和您的网络与集群节点的负载。如果设置为0,当任何节点具"
+"有高负载时,集群将施加一个动态计算的限制。"
+
+#: lib/common/options.c:408
+msgid ""
+"The number of live migration actions that the cluster is allowed to execute "
+"in parallel on a node (-1 means no limit)"
+msgstr "允许集群在一个节点上并行执行的实时迁移操作的数量(-1表示没有限制)"
-#: daemons/fenced/pacemaker-fenced.c:536
-msgid "Advanced use only: An alternate parameter to supply instead of 'port'"
-msgstr "仅高级使用:使用替代的参数名,而不是'port'"
+#: lib/common/options.c:427
+msgid "Maximum IPC message backlog before disconnecting a cluster daemon"
+msgstr "断开集群守护程序之前的最大IPC消息积压"
-#: daemons/fenced/pacemaker-fenced.c:537
+#: lib/common/options.c:428
msgid ""
-"some devices do not support the standard 'port' parameter or may provide "
+"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)."
+msgstr ""
+"如果日志中有针对集群守护程序PID的消息“Evicting client”,(则建议将值设为集群"
+"中的资源数量乘以节点数量)"
+
+#: lib/common/options.c:438
+#, fuzzy
+msgid "Whether the cluster should stop all active resources"
+msgstr "集群是否在启动期间检查运行资源"
+
+#: lib/common/options.c:445
+msgid "Whether to stop resources that were removed from the configuration"
+msgstr "是否停止配置已被删除的资源"
+
+#: lib/common/options.c:453
+msgid "Whether to cancel recurring actions removed from the configuration"
+msgstr "是否取消配置已被删除的的重复操作"
+
+#: lib/common/options.c:461
+#, fuzzy
+msgid "Whether to remove stopped resources from the executor"
+msgstr "是否从pacemaker-execd 守护进程中清除已停止的资源"
+
+#: lib/common/options.c:462
+#, fuzzy
+msgid "Values other than default are poorly tested and potentially dangerous."
+msgstr "非默认值未经过充分的测试,有潜在的风险。该选项将在未来的版本中删除。"
+
+#: lib/common/options.c:471
+msgid "The number of scheduler inputs resulting in errors to save"
+msgstr "保存导致错误的调度程序输入的数量"
+
+#: lib/common/options.c:472 lib/common/options.c:479 lib/common/options.c:486
+msgid "Zero to disable, -1 to store unlimited."
+msgstr "零表示禁用,-1表示存储不受限制。"
+
+#: lib/common/options.c:478
+msgid "The number of scheduler inputs resulting in warnings to save"
+msgstr "保存导致警告的调度程序输入的数量"
+
+#: lib/common/options.c:485
+msgid "The number of scheduler inputs without errors or warnings to save"
+msgstr "保存没有错误或警告的调度程序输入的数量"
+
+#: lib/common/options.c:497
+#, fuzzy
+msgid "How cluster should react to node health attributes"
+msgstr "集群节点对节点健康属性如何反应"
+
+#: lib/common/options.c:498
+msgid ""
+"Requires external entities to create node attributes (named with the prefix "
+"\"#health\") with values \"red\", \"yellow\", or \"green\"."
+msgstr ""
+"需要外部实体创建具有“red”,“yellow”或“green”值的节点属性(前缀为“#health”)"
+
+#: lib/common/options.c:506
+msgid "Base health score assigned to a node"
+msgstr "分配给节点的基本健康分数"
+
+#: lib/common/options.c:507
+msgid "Only used when \"node-health-strategy\" is set to \"progressive\"."
+msgstr "仅在“node-health-strategy”设置为“progressive”时使用。"
+
+#: lib/common/options.c:514
+msgid "The score to use for a node health attribute whose value is \"green\""
+msgstr "为节点健康属性值为“green”所使用的分数"
+
+#: lib/common/options.c:516 lib/common/options.c:525 lib/common/options.c:534
+msgid ""
+"Only used when \"node-health-strategy\" is set to \"custom\" or \"progressive"
+"\"."
+msgstr "仅在“node-health-strategy”设置为“custom”或“progressive”时使用。"
+
+#: lib/common/options.c:523
+msgid "The score to use for a node health attribute whose value is \"yellow\""
+msgstr "为节点健康属性值为“yellow”所使用的分数"
+
+#: lib/common/options.c:532
+msgid "The score to use for a node health attribute whose value is \"red\""
+msgstr "为节点健康属性值为“red”所使用的分数"
+
+#: lib/common/options.c:545
+#, fuzzy
+msgid "How the cluster should allocate resources to nodes"
+msgstr "集群应该如何分配资源到节点"
+
+#: lib/common/options.c:563
+#, fuzzy
+msgid "An alternate parameter to supply instead of 'port'"
+msgstr "用于替代 'port' 的其它参数"
+
+#: lib/common/options.c:564
+#, fuzzy
+msgid ""
+"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."
+"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."
msgstr ""
-"一些设备不支持标准的'port'参数,或者可能提供其他参数。使用此选项可指定一个该"
-"设备专用的参数名,该参数用于标识需要fence的机器。值none可以用于告诉集群不要提"
-"供任何其他的参数。"
+"一些设备不支持标准的 'port' 参数, 或者可能会提供其它的参数. 使用此选项可指定"
+"一个替代的, 该设备专用的参数, 该参数应该指出需要 fence 的机器. 可以使用 "
+"\"none\" 值用于告诉集群不要提供任何其它的参数. "
-#: daemons/fenced/pacemaker-fenced.c:546
+#: lib/common/options.c:574
+#, fuzzy
msgid ""
-"A mapping of host names to ports numbers for devices that do not support "
-"host names."
-msgstr "为不支持主机名的设备提供主机名到端口号的映射。"
+"A mapping of node names to port numbers for devices that do not support node "
+"names."
+msgstr "为不支持主机名的设备提供主机名到端口号的映射. "
-#: daemons/fenced/pacemaker-fenced.c:547
+#: lib/common/options.c:576
+#, fuzzy
msgid ""
-"Eg. node1:1;node2:2,3 would tell the cluster to use port 1 for node1 and "
-"ports 2 and 3 for node2"
+"For example, \"node1:1;node2:2,3\" would tell the cluster to use port 1 for "
+"node1 and ports 2 and 3 for node2."
msgstr ""
-"例如 node1:1;node2:2,3,将会告诉集群对node1使用端口1,对node2使用端口2和3 "
+"例如, \"node1:1;node2:2,3\" 将会告诉集群对node1使用端口1, 对node2使用端口2和"
+"3."
-#: daemons/fenced/pacemaker-fenced.c:551
-msgid "Eg. node1,node2,node3"
-msgstr "例如 node1,node2,node3"
+#: lib/common/options.c:583
+msgid "Nodes targeted by this device"
+msgstr "此设备针对的节点"
-#: daemons/fenced/pacemaker-fenced.c:552
+#: lib/common/options.c:584
+#, fuzzy
msgid ""
-"A list of machines controlled by this device (Optional unless "
-"pcmk_host_list=static-list)"
-msgstr "该设备控制的机器列表(可选参数,除非 pcmk_host_list 设置为 static-list)"
+"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."
+msgstr ""
+"此设备可以针对的节点列表,节点之间用逗号分隔(例如,node1,node2, node3).如果"
+"pcmk_host_list=\"static-list\")"
-#: daemons/fenced/pacemaker-fenced.c:557
-msgid "How to determine which machines are controlled by the device."
-msgstr "如何确定设备控制哪些机器。"
+#: lib/common/options.c:594
+#, fuzzy
+msgid "How to determine which nodes can be targeted by the device"
+msgstr "如何确定设备可以针对哪些节点"
-#: daemons/fenced/pacemaker-fenced.c:558
+#: lib/common/options.c:595
+#, fuzzy
msgid ""
-"Allowed values: dynamic-list (query the device via the 'list' command), "
-"static-list (check the pcmk_host_list attribute), status (query the device "
-"via the 'status' command), none (assume every device can fence every machine)"
+"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\""
msgstr ""
-"允许的值:dynamic-list(通过'list'命令查询设备),static-list(检查"
-"pcmk_host_list属性),status(通过'status'命令查询设备),none(假设每个设备"
-"都可fence 每台机器 )"
+"选项值 \"dynamic-list\" 表示通过 'list' 命令查询设备; 选项值 \"static-list"
+"\"表示检查 pcmk_host_list 属性; 选项值 \"status\" 表示通过 'status' 命令查询"
+"设备; 或使用选项值 \"none\" 假设每个设备都可以 fence 所有节点. 如果"
+"\"pcmk_host_map\"或\"pcmk_host_list\"被设置,默认值为\"static-list\";否则,"
+"如果设备支持列表操作,则为\"dynamic-list\";如果设备支持状态操作,则为"
+"\"status\";否则为\"none\""
-#: daemons/fenced/pacemaker-fenced.c:567 daemons/fenced/pacemaker-fenced.c:576
-msgid "Enable a base delay for fencing actions and specify base delay value."
-msgstr "在执行 fencing 操作前启用不超过指定时间的延迟。"
+#: lib/common/options.c:608
+msgid ""
+"Enable a delay of no more than the time specified before executing fencing "
+"actions."
+msgstr "在执行 fence 操作前启用不超过指定时间的延迟"
-#: daemons/fenced/pacemaker-fenced.c:568
+#: lib/common/options.c:610
msgid ""
"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."
msgstr ""
-"在执行 fencing 操作前启用不超过指定时间的延迟。 Pacemaker通过获取"
-"pcmk_delay_base的值并添加随机延迟值来得出总体延迟,从而使总和保持在此最大值以"
-"下。"
+"在执行 fence 操作前启用不超过指定时间的延迟. Pacemaker通过获取 "
+"pcmk_delay_base 的值并添加随机延迟值来得出总延迟, 并且确保总和不超过此最大值."
-#: daemons/fenced/pacemaker-fenced.c:578
+#: lib/common/options.c:619
+msgid "Enable a base delay for fencing actions and specify base delay value."
+msgstr "为 fence 操作启用一个指定的基础延迟. "
+
+#: lib/common/options.c:621
+#, fuzzy
msgid ""
"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 "
+"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 per target."
+"node2:5\") to set a different value for each target."
msgstr ""
-"这使fencing 操作启用静态延迟,这可以帮助避免\"death matches\"即两个节点试图同"
-"时互相fence.如果还使用了pcmk_delay_max,则将添加随机延迟,以使总延迟保持在该"
-"值以下。可以将其设置为单个时间值,以应用于该设备针对的任何节点(适用于为每个"
-"目标分别配置了各自的设备的情况), 或着设置为一个节点映射 (例如,\"node1:1s;"
-"node2:5\")从而为每个目标设置不同值。"
+"这为 fence 操作启用一个静态延迟, 这有助于避免 \"death matches\" 即两个节点同"
+"时尝试互相 fence. 如果还同时使用了pcmk_delay_max, 则会添加一个随机延迟, 并确"
+"保总延迟保持在该值以下. 可以将其设置为单个时间值, 以应用于该设备的所有目标节"
+"点 (如果为每个目标节点都配置了单独的设备的情况下, 这很有用) 或设置成一个节点"
+"映射形式 (例如,\"node1:1s;node2:5\") 从而为每个目标节点设置不同值. "
-#: daemons/fenced/pacemaker-fenced.c:590
+#: lib/common/options.c:634
msgid ""
"The maximum number of actions can be performed in parallel on this device"
msgstr "可以在该设备上并发执行的最多操作数量"
-#: daemons/fenced/pacemaker-fenced.c:591
+#: lib/common/options.c:636
+#, fuzzy
msgid ""
-"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. -1 is unlimited."
+"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."
msgstr ""
-"需要首先配置集群属性 concurrent-fencing=true 。然后使用此参数指定可以在该设备"
-"上并发执行的最多操作数量。 -1 代表没有限制"
+"需要先配置集群属性 concurrent-fencing=\"true\". 然后使用此参数指定可以在该设"
+"备上并发执行的最多操作数量. -1 表示可以并行执行无限数量的操作. "
-#: daemons/fenced/pacemaker-fenced.c:597
-msgid "Advanced use only: An alternate command to run instead of 'reboot'"
-msgstr "仅高级使用:运行替代命令,而不是'reboot'"
+#: lib/common/options.c:646
+#, fuzzy
+msgid "An alternate command to run instead of 'reboot'"
+msgstr "运行替代命令,而不是'reboot'"
-#: daemons/fenced/pacemaker-fenced.c:598
+#: lib/common/options.c:647
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.\n"
-"Use this to specify an alternate, device-specific, command that implements "
-"the 'reboot' action."
+"ones. Use this to specify an alternate, device-specific, command that "
+"implements the 'reboot' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可以指定一个该设备特定的"
"替代命令,用来实现'reboot'操作。"
-#: daemons/fenced/pacemaker-fenced.c:603
+#: lib/common/options.c:655
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for reboot actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于'reboot' 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'reboot' actions instead of stonith-"
+"timeout"
+msgstr "指定用于'reboot' 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:604
+#: lib/common/options.c:657
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.Use this to "
+"Some devices need much more/less time to complete than normal. Use this to "
"specify an alternate, device-specific, timeout for 'reboot' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'reboot'操作的该设备特定的替代超时。"
-#: daemons/fenced/pacemaker-fenced.c:609
+#: lib/common/options.c:665
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'reboot' command "
-"within the timeout period"
-msgstr "仅高级使用:在超时前重试'reboot'命令的最大次数"
+"The maximum number of times to try the 'reboot' command within the timeout "
+"period"
+msgstr "在超时前重试'reboot'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:610
+#: lib/common/options.c:667
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'reboot' actions before giving up."
+"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."
msgstr ""
"一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' ,因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'reboot' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:617
-msgid "Advanced use only: An alternate command to run instead of 'off'"
-msgstr "仅高级使用:运行替代命令,而不是'off'"
+#: lib/common/options.c:677
+#, fuzzy
+msgid "An alternate command to run instead of 'off'"
+msgstr "运行替代命令,而不是'off'"
-#: daemons/fenced/pacemaker-fenced.c:618
+#: lib/common/options.c:678
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.Use this to specify an alternate, device-specific, command that "
+"ones. Use this to specify an alternate, device-specific, command that "
"implements the 'off' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可指定一个该设备专用的替代"
"命令,用来实现'off'操作。"
-#: daemons/fenced/pacemaker-fenced.c:623
+#: lib/common/options.c:686
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for off actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于off 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'off' actions instead of stonith-"
+"timeout"
+msgstr "指定用于off 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:624
+#: lib/common/options.c:688
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.Use this to "
+"Some devices need much more/less time to complete than normal. Use this to "
"specify an alternate, device-specific, timeout for 'off' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'off'操作的该设备特定的替代超时。"
-#: daemons/fenced/pacemaker-fenced.c:629
+#: lib/common/options.c:696
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'off' command "
-"within the timeout period"
-msgstr "仅高级使用:在超时前重试'off'命令的最大次数"
+"The maximum number of times to try the 'off' command within the timeout "
+"period"
+msgstr "在超时前重试'off'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:630
+#: lib/common/options.c:698
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'off' actions before giving up."
+"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."
msgstr ""
" 一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' , 因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'off' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:637
-msgid "Advanced use only: An alternate command to run instead of 'on'"
+#: lib/common/options.c:708
+#, fuzzy
+msgid "An alternate command to run instead of 'on'"
msgstr "仅高级使用:运行替代命令,而不是'on'"
-#: daemons/fenced/pacemaker-fenced.c:638
+#: lib/common/options.c:709
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.Use this to specify an alternate, device-specific, command that "
+"ones. Use this to specify an alternate, device-specific, command that "
"implements the 'on' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可指定一个该设备特定的替"
"代命令,用来实现'on'操作。"
-#: daemons/fenced/pacemaker-fenced.c:643
+#: lib/common/options.c:717
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for on actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于on 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'on' actions instead of stonith-"
+"timeout"
+msgstr "指定用于on 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:644
+#: lib/common/options.c:719
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.Use this to "
+"Some devices need much more/less time to complete than normal. Use this to "
"specify an alternate, device-specific, timeout for 'on' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'on'操作的该设备特定的替代超时。"
-#: daemons/fenced/pacemaker-fenced.c:649
+#: lib/common/options.c:727
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'on' command "
-"within the timeout period"
-msgstr "仅高级使用:在超时前重试'on'命令的最大次数"
+"The maximum number of times to try the 'on' command within the timeout period"
+msgstr "在超时前重试'on'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:650
+#: lib/common/options.c:729
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'on' actions before giving up."
+"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."
msgstr ""
" 一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' , 因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'on' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:657
-msgid "Advanced use only: An alternate command to run instead of 'list'"
-msgstr "仅高级使用:运行替代命令,而不是'list'"
+#: lib/common/options.c:739
+#, fuzzy
+msgid "An alternate command to run instead of 'list'"
+msgstr "运行替代命令,而不是'list'"
-#: daemons/fenced/pacemaker-fenced.c:658
+#: lib/common/options.c:740
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.Use this to specify an alternate, device-specific, command that "
+"ones. Use this to specify an alternate, device-specific, command that "
"implements the 'list' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可指定一个该设备特定的替"
"代命令,用来实现'list'操作。"
-#: daemons/fenced/pacemaker-fenced.c:663
+#: lib/common/options.c:748
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for list actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于list 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'list' actions instead of stonith-"
+"timeout"
+msgstr "指定用于list 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:664
+#: lib/common/options.c:750
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.Use this to "
+"Some devices need much more/less time to complete than normal. Use this to "
"specify an alternate, device-specific, timeout for 'list' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'list'操作的该设备特定的替代超时。"
-#: daemons/fenced/pacemaker-fenced.c:669
+#: lib/common/options.c:758
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'list' command "
-"within the timeout period"
-msgstr "仅高级使用:在超时前重试'list'命令的最大次数"
+"The maximum number of times to try the 'list' command within the timeout "
+"period"
+msgstr "在超时前重试'list'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:670
+#: lib/common/options.c:760
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'list' actions before giving up."
+"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."
msgstr ""
" 一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' , 因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'list' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:677
-msgid "Advanced use only: An alternate command to run instead of 'monitor'"
-msgstr "仅高级使用:运行替代命令,而不是'monitor'"
+#: lib/common/options.c:770
+#, fuzzy
+msgid "An alternate command to run instead of 'monitor'"
+msgstr "运行替代命令,而不是'monitor'"
-#: daemons/fenced/pacemaker-fenced.c:678
+#: lib/common/options.c:771
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.Use this to specify an alternate, device-specific, command that "
+"ones. Use this to specify an alternate, device-specific, command that "
"implements the 'monitor' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可指定一个该设备特定的替"
"代命令,用来实现'monitor'操作。"
-#: daemons/fenced/pacemaker-fenced.c:683
+#: lib/common/options.c:779
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for monitor actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于monitor 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'monitor' actions instead of stonith-"
+"timeout"
+msgstr "指定用于monitor 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:684
+#: lib/common/options.c:781
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.\n"
-"Use this to specify an alternate, device-specific, timeout for 'monitor' "
-"actions."
+"Some devices need much more/less time to complete than normal. Use this to "
+"specify an alternate, device-specific, timeout for 'monitor' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'monitor'操作的该设备特定的替代超时。"
-#: daemons/fenced/pacemaker-fenced.c:689
+#: lib/common/options.c:789
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'monitor' "
-"command within the timeout period"
-msgstr "仅高级使用:在超时前重试'monitor'命令的最大次数"
+"The maximum number of times to try the 'monitor' command within the timeout "
+"period"
+msgstr "在超时前重试'monitor'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:690
+#: lib/common/options.c:791
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'monitor' actions before giving up."
+"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."
msgstr ""
" 一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' , 因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'monitor' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:697
-msgid "Advanced use only: An alternate command to run instead of 'status'"
-msgstr "仅高级使用:运行替代命令,而不是'status'"
+#: lib/common/options.c:801
+#, fuzzy
+msgid "An alternate command to run instead of 'status'"
+msgstr "运行替代命令,而不是'status'"
-#: daemons/fenced/pacemaker-fenced.c:698
+#: lib/common/options.c:802
+#, fuzzy
msgid ""
"Some devices do not support the standard commands or may provide additional "
-"ones.Use this to specify an alternate, device-specific, command that "
+"ones. Use this to specify an alternate, device-specific, command that "
"implements the 'status' action."
msgstr ""
"一些设备不支持标准命令或可能提供其他命令,使用此选项可指定一个该设备特定的替"
"代命令,用来实现'status'操作。"
-#: daemons/fenced/pacemaker-fenced.c:703
+#: lib/common/options.c:810
+#, fuzzy
msgid ""
-"Advanced use only: Specify an alternate timeout to use for status actions "
-"instead of stonith-timeout"
-msgstr "仅高级使用:指定用于status 操作的替代超时,而不是stonith-timeout"
+"Specify an alternate timeout to use for 'status' actions instead of stonith-"
+"timeout"
+msgstr "指定用于status 操作的替代超时,而不是stonith-timeout"
-#: daemons/fenced/pacemaker-fenced.c:704
+#: lib/common/options.c:812
+#, fuzzy
msgid ""
-"Some devices need much more/less time to complete than normal.Use this to "
+"Some devices need much more/less time to complete than normal. Use this to "
"specify an alternate, device-specific, timeout for 'status' actions."
msgstr ""
"一些设备需要比正常情况下更多或更少的时间来完成操作,使用此选项指定一个用"
"于'status'操作的该设备特定的替代超时"
-#: daemons/fenced/pacemaker-fenced.c:709
+#: lib/common/options.c:820
+#, fuzzy
msgid ""
-"Advanced use only: The maximum number of times to retry the 'status' command "
-"within the timeout period"
+"The maximum number of times to try the 'status' command within the timeout "
+"period"
msgstr "仅高级使用:在超时前重试'status'命令的最大次数"
-#: daemons/fenced/pacemaker-fenced.c:710
+#: lib/common/options.c:822
+#, fuzzy
msgid ""
-"Some devices do not support multiple connections. Operations may 'fail' if "
-"the device is busy with another task so Pacemaker will automatically retry "
-"the operation, if there is time remaining. Use this option to alter the "
-"number of times Pacemaker retries 'status' actions before giving up."
+"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."
msgstr ""
" 一些设备不支持多个连接。 如果设备忙于另一个任务,则操作可能会'失败' , 因此"
"Pacemaker将自动重试(如果时间允许)。 使用此选项更改Pacemaker在放弃之前重"
"试'status' 操作的次数."
-#: daemons/fenced/pacemaker-fenced.c:719
-msgid "Instance attributes available for all \"stonith\"-class resources"
-msgstr " 可用于所有stonith类资源的实例属性"
-
-#: daemons/fenced/pacemaker-fenced.c:721
-msgid ""
-"Instance attributes available for all \"stonith\"-class resources and used "
-"by Pacemaker's fence daemon, formerly known as stonithd"
+#: lib/common/options.c:843
+msgid "Resource assignment priority"
msgstr ""
-" 可用于所有stonith类资源的实例属性,并由Pacemaker的fence守护程序使用(以前称"
-"为stonithd)"
-
-#: daemons/fenced/pacemaker-fenced.c:734
-msgid "Deprecated (will be removed in a future release)"
-msgstr "已弃用(将在未来版本中删除)"
-
-#: daemons/fenced/pacemaker-fenced.c:737
-msgid "Intended for use in regression testing only"
-msgstr "仅适用于回归测试"
-
-#: daemons/fenced/pacemaker-fenced.c:740
-msgid "Send logs to the additional named logfile"
-msgstr "将日志发送到其他命名日志文件"
-
-#: lib/cib/cib_utils.c:875
-msgid "Enable Access Control Lists (ACLs) for the CIB"
-msgstr "为CIB启用访问控制列表(ACL)"
-#: lib/cib/cib_utils.c:881
-msgid "Maximum IPC message backlog before disconnecting a cluster daemon"
-msgstr "断开集群守护程序之前的最大IPC消息积压"
-
-#: lib/cib/cib_utils.c:882
+#: lib/common/options.c:844
msgid ""
-"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)."
+"If not all resources can be active, the cluster will stop lower-priority "
+"resources in order to keep higher-priority ones active."
msgstr ""
-"如果日志中有针对集群守护程序PID的消息“Evicting client”,(则建议将值设为集群"
-"中的资源数量乘以节点数量)"
-#: lib/common/options.c:414
-msgid " Allowed values: "
-msgstr " 允许的值: "
-
-#: lib/common/cmdline.c:70
-msgid "Display software version and exit"
-msgstr "显示软件版本信息"
-
-#: lib/common/cmdline.c:73
-msgid "Increase debug output (may be specified multiple times)"
-msgstr "显示更多调试信息(可多次指定)"
-
-#: lib/common/cmdline.c:92
-msgid "FORMAT"
-msgstr "格式"
-
-#: lib/common/cmdline.c:94
-msgid "Specify file name for output (or \"-\" for stdout)"
-msgstr "指定输出的文件名 或指定'-' 表示标准输出"
-
-#: lib/common/cmdline.c:94
-msgid "DEST"
-msgstr "目标"
-
-#: lib/common/cmdline.c:100
-msgid "Output Options:"
-msgstr "输出选项"
-
-#: lib/common/cmdline.c:100
-msgid "Show output help"
-msgstr "显示输出帮助"
-
-#: lib/pengine/common.c:46
-msgid "Whether resources can run on any node by default"
-msgstr "资源是否默认可以在任何节点上运行"
+#: lib/common/options.c:852
+msgid "Default value for influence in colocation constraints"
+msgstr ""
-#: lib/pengine/common.c:52
+#: lib/common/options.c:853
msgid ""
-"Whether the cluster should refrain from monitoring, starting, and stopping "
-"resources"
-msgstr "集群是否应避免监视,启动和停止资源"
+"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."
+msgstr ""
-#: lib/pengine/common.c:59
-msgid ""
-"Whether a start failure should prevent a resource from being recovered on "
-"the same node"
-msgstr "是否避免在同一节点上重启启动失败的资源"
+#: lib/common/options.c:863
+#, fuzzy
+msgid "State the cluster should attempt to keep this resource in"
+msgstr "集群是否在启动期间检查运行资源"
-#: lib/pengine/common.c:61
+#: lib/common/options.c:864
msgid ""
-"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."
+"\"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\"."
msgstr ""
-"当为true,如果资源启动失败,集群将立即禁止节点启动该资源,当为false,集群将根"
-"据其迁移阈值来检查资源的失败计数。"
-#: lib/pengine/common.c:68
-msgid "Whether the cluster should check for active resources during start-up"
+#: lib/common/options.c:875
+#, fuzzy
+msgid "Whether the cluster is allowed to actively change the resource's state"
msgstr "集群是否在启动期间检查运行资源"
-#: lib/pengine/common.c:99
-msgid ""
-"*** Advanced Use Only *** Whether nodes may be fenced as part of recovery"
-msgstr "*** Advanced Use Only *** 节点是否可以被 fence 以作为集群恢复的一部分"
-
-#: lib/pengine/common.c:101
+#: lib/common/options.c:877
msgid ""
-"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."
+"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."
msgstr ""
-"如果为false,则立即假定无响应的节点是无害的,并且可以在其他位置恢复在其上活动"
-"的资源。 这可能会导致 \"split-brain\" 情况,可能导致数据丢失和/或服务不可用。"
-#: lib/pengine/common.c:109
+#: lib/common/options.c:887
msgid ""
-"Action to send to fence device when a node needs to be fenced (\"poweroff\" "
-"is a deprecated alias for \"off\")"
-msgstr "发送到 fence 设备的操作( \"poweroff\" 是 \"off \"的别名,不建议使用)"
-
-#: lib/pengine/common.c:116
-msgid "*** Advanced Use Only *** Unused by Pacemaker"
-msgstr "*** Advanced Use Only *** pacemaker未使用"
+"If true, the cluster will not schedule any actions involving the resource"
+msgstr ""
-#: lib/pengine/common.c:117
+#: lib/common/options.c:889
msgid ""
-"This value is not used by Pacemaker, but is kept for backward compatibility, "
-"and certain legacy fence agents might use it."
+"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."
msgstr ""
-"Pacemaker不使用此值,但保留此值是为了向后兼容,某些传统的fence 代理可能会使用"
-"它。"
-#: lib/pengine/common.c:123
-msgid "Whether watchdog integration is enabled"
-msgstr "是否启用watchdog集成设置"
+#: lib/common/options.c:899
+msgid "Score to add to the current node when a resource is already active"
+msgstr ""
-#: lib/pengine/common.c:124
+#: lib/common/options.c:901
msgid ""
-"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."
+"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."
msgstr ""
-"这是由集群检测是否正在使用 SBD 并自动设置。用户配置的值将被忽略。如果使用无"
-"盘 SBD 并且 stonith-watchdog-timeout 不为零时,此选项为 true 才有实际意义。在"
-"这种情况下,无需明确配置fence资源,如果需要fence时,基于watchdog的自我fence会"
-"通过SBD执行。"
-#: lib/pengine/common.c:134
-msgid "Allow performing fencing operations in parallel"
-msgstr "允许并行执行 fencing 操作"
+#: lib/common/options.c:914
+msgid "Conditions under which the resource can be started"
+msgstr ""
-#: lib/pengine/common.c:140
-msgid "*** Advanced Use Only *** Whether to fence unseen nodes at start-up"
-msgstr "*** 仅高级使用 *** 是否在启动时fence不可见节点"
+#: lib/common/options.c:915
+msgid ""
+"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\"."
+msgstr ""
-#: lib/pengine/common.c:141
+#: lib/common/options.c:936
msgid ""
-"Setting this to false may lead to a \"split-brain\" situation,potentially "
-"leading to data loss and/or service unavailability."
+"Number of failures on a node before the resource becomes ineligible to run "
+"there."
msgstr ""
-"将此设置为 false 可能会导致 \"split-brain\" 的情况,可能导致数据丢失和/或服务"
-"不可用。"
-#: lib/pengine/common.c:147
+#: lib/common/options.c:938
msgid ""
-"Apply fencing delay targeting the lost nodes with the highest total resource "
-"priority"
-msgstr "针对具有最高总资源优先级的丢失节点应用fencing延迟"
+"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."
+msgstr ""
-#: lib/pengine/common.c:148
-msgid ""
-"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."
+#: lib/common/options.c:952
+msgid "Number of seconds before acting as if a failure had not occurred"
msgstr ""
-"如果我们所在的集群分区并不拥有大多数集群节点,则针对丢失节点的fence操作应用指"
-"定的延迟,这样更重要的节点就能够赢得fence竞赛。这对于双节点集群在split-brain"
-"状况下尤其有意义。如果基本优先级不为0,在计算时主资源实例获得基本优先级+1。任"
-"何对于相应的 fence 资源由 pcmk_delay_base/max 配置所引入的静态/随机延迟会被添"
-"加到此延迟。为了安全, 这个延迟应该明显大于 pcmk_delay_base/max 的最大设置值,"
-"例如两倍。默认情况下,优先级fencing延迟已禁用。"
-#: lib/pengine/common.c:175
-msgid "Maximum time for node-to-node communication"
-msgstr "最大节点间通信时间"
+#: lib/common/options.c:953
+msgid ""
+"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."
+msgstr ""
-#: lib/pengine/common.c:176
+#: lib/common/options.c:964
msgid ""
-"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."
+"What to do if the cluster finds the resource active on more than one node"
+msgstr ""
+
+#: lib/common/options.c:966
+msgid ""
+"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.)"
msgstr ""
-"如果一个操作未在该时间内(并且考虑操作本身的超时时长)从执行该操作的节点获得"
-"响应,则会被选为指定控制器(DC)的节点认定为失败。\"正确\" 值将取决于速度和您"
-"的网络和集群节点的负载。"
-#: lib/pengine/common.c:185
+#: lib/common/options.c:985
#, fuzzy
msgid ""
-"Maximum number of jobs that the cluster may execute in parallel across all "
-"nodes"
-msgstr "集群可以在所有节点上并发执行的最大作业数"
+"Whether the cluster should try to \"live migrate\" this resource when it "
+"needs to be moved"
+msgstr "集群是否在启动期间检查运行资源"
-#: lib/pengine/common.c:187
+#: lib/common/options.c:987
msgid ""
-"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."
+"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."
msgstr ""
-"\"正确\" 值将取决于速度和您的网络与集群节点的负载。如果设置为0,当任何节点具"
-"有高负载时,集群将施加一个动态计算的限制。"
-#: lib/pengine/common.c:195
+#: lib/common/options.c:996
msgid ""
-"The number of live migration actions that the cluster is allowed to execute "
-"in parallel on a node (-1 means no limit)"
-msgstr "允许集群在一个节点上并行执行的实时迁移操作的数量(-1表示没有限制)"
+"Whether the resource should be allowed to run on a node even if the node's "
+"health score would otherwise prevent it"
+msgstr ""
-#: lib/pengine/common.c:203
+#: lib/common/options.c:1004
#, fuzzy
-msgid "Whether the cluster should stop all active resources"
-msgstr "集群是否在启动期间检查运行资源"
-
-#: lib/pengine/common.c:209
-msgid "Whether to stop resources that were removed from the configuration"
-msgstr "是否停止配置已被删除的资源"
+msgid "Where to check user-defined node attributes"
+msgstr "*** 仅高级使用 *** 是否在启动时fence不可见节点"
-#: lib/pengine/common.c:215
-msgid "Whether to cancel recurring actions removed from the configuration"
-msgstr "是否取消配置已被删除的的重复操作"
+#: lib/common/options.c:1005
+msgid ""
+"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)."
+msgstr ""
-#: lib/pengine/common.c:221
+#: lib/common/options.c:1018
msgid ""
-"*** Deprecated *** Whether to remove stopped resources from the executor"
-msgstr "***不推荐***是否从pacemaker-execd 守护进程中清除已停止的资源"
+"Name of the Pacemaker Remote guest node this resource is associated with, if "
+"any"
+msgstr ""
-#: lib/pengine/common.c:223
+#: lib/common/options.c:1020
msgid ""
-"Values other than default are poorly tested and potentially dangerous. This "
-"option will be removed in a future release."
-msgstr "非默认值未经过充分的测试,有潜在的风险。该选项将在未来的版本中删除。"
+"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."
+msgstr ""
-#: lib/pengine/common.c:231
-msgid "The number of scheduler inputs resulting in errors to save"
-msgstr "保存导致错误的调度程序输入的数量"
+#: lib/common/options.c:1032
+msgid ""
+"If remote-node is specified, the IP address or hostname used to connect to "
+"the guest via Pacemaker Remote"
+msgstr ""
-#: lib/pengine/common.c:232 lib/pengine/common.c:238 lib/pengine/common.c:244
-msgid "Zero to disable, -1 to store unlimited."
-msgstr "零表示禁用,-1表示存储不受限制。"
+#: lib/common/options.c:1034
+msgid ""
+"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."
+msgstr ""
-#: lib/pengine/common.c:237
-msgid "The number of scheduler inputs resulting in warnings to save"
-msgstr "保存导致警告的调度程序输入的数量"
+#: lib/common/options.c:1044
+msgid ""
+"If remote-node is specified, port on the guest used for its Pacemaker Remote "
+"connection"
+msgstr ""
-#: lib/pengine/common.c:243
-msgid "The number of scheduler inputs without errors or warnings to save"
-msgstr "保存没有错误或警告的调度程序输入的数量"
+#: lib/common/options.c:1046
+msgid ""
+"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."
+msgstr ""
-#: lib/pengine/common.c:254
-#, fuzzy
-msgid "How cluster should react to node health attributes"
-msgstr "集群节点对节点健康属性如何反应"
+#: lib/common/options.c:1054
+msgid ""
+"If remote-node is specified, how long before a pending Pacemaker Remote "
+"guest connection times out."
+msgstr ""
-#: lib/pengine/common.c:255
+#: lib/common/options.c:1062
msgid ""
-"Requires external entities to create node attributes (named with the prefix "
-"\"#health\") with values \"red\", \"yellow\", or \"green\"."
+"If remote-node is specified, this acts as the allow-migrate meta-attribute "
+"for the implicit remote connection resource (ocf:pacemaker:remote)."
msgstr ""
-"需要外部实体创建具有“red”,“yellow”或“green”值的节点属性(前缀为“#health”)"
-#: lib/pengine/common.c:262
-msgid "Base health score assigned to a node"
-msgstr "分配给节点的基本健康分数"
+#: lib/common/cmdline.c:70
+msgid "Display software version and exit"
+msgstr "显示软件版本信息"
-#: lib/pengine/common.c:263
-msgid "Only used when \"node-health-strategy\" is set to \"progressive\"."
-msgstr "仅在“node-health-strategy”设置为“progressive”时使用。"
+#: lib/common/cmdline.c:73
+msgid "Increase debug output (may be specified multiple times)"
+msgstr "显示更多调试信息(可多次指定)"
-#: lib/pengine/common.c:268
-msgid "The score to use for a node health attribute whose value is \"green\""
-msgstr "为节点健康属性值为“green”所使用的分数"
+#: lib/common/cmdline.c:92
+msgid "FORMAT"
+msgstr "格式"
-#: lib/pengine/common.c:269 lib/pengine/common.c:275 lib/pengine/common.c:281
-msgid ""
-"Only used when \"node-health-strategy\" is set to \"custom\" or \"progressive"
-"\"."
-msgstr "仅在“node-health-strategy”设置为“custom”或“progressive”时使用。"
+#: lib/common/cmdline.c:94
+msgid "Specify file name for output (or \"-\" for stdout)"
+msgstr "指定输出的文件名 或指定'-' 表示标准输出"
-#: lib/pengine/common.c:274
-msgid "The score to use for a node health attribute whose value is \"yellow\""
-msgstr "为节点健康属性值为“yellow”所使用的分数"
+#: lib/common/cmdline.c:94
+msgid "DEST"
+msgstr "目标"
-#: lib/pengine/common.c:280
-msgid "The score to use for a node health attribute whose value is \"red\""
-msgstr "为节点健康属性值为“red”所使用的分数"
+#: lib/common/cmdline.c:100
+msgid "Output Options:"
+msgstr "输出选项"
-#: lib/pengine/common.c:289
-#, fuzzy
-msgid "How the cluster should allocate resources to nodes"
-msgstr "集群应该如何分配资源到节点"
+#: lib/common/cmdline.c:100
+msgid "Show output help"
+msgstr "显示输出帮助"
-#: tools/crm_resource.c:258
+#: tools/crm_resource.c:204
#, c-format
msgid "Aborting because no messages received in %d seconds"
msgstr "中止,因为在%d秒内没有接收到消息"
-#: tools/crm_resource.c:921
+#: tools/crm_resource.c:374
#, c-format
msgid "Invalid check level setting: %s"
msgstr "无效的检查级别设置:%s"
-#: tools/crm_resource.c:1008
+#: tools/crm_resource.c:891
#, c-format
msgid ""
"Resource '%s' not moved: active in %d locations (promoted in %d).\n"
@@ -951,7 +1241,7 @@ msgstr ""
"若要阻止'%s'在特定位置运行,请指定一个节点。若要防止'%s'在指定位置升级,指定"
"一个节点并使用--promoted选项"
-#: tools/crm_resource.c:1019
+#: tools/crm_resource.c:902
#, c-format
msgid ""
"Resource '%s' not moved: active in %d locations.\n"
@@ -960,141 +1250,167 @@ msgstr ""
"资源%s未移动:在%d个位置运行\n"
"若要防止'%s'运行在特定位置,指定一个节点"
-#: tools/crm_resource.c:1096
+#: tools/crm_resource.c:979
#, c-format
msgid "Could not get modified CIB: %s\n"
msgstr "无法获得修改的CIB:%s\n"
-#: tools/crm_resource.c:1174
+#: tools/crm_resource.c:1077
#, c-format
msgid "No cluster connection to Pacemaker Remote node %s detected"
msgstr "未检测到至pacemaker远程节点%s的集群连接"
-#: tools/crm_resource.c:1235
+#: tools/crm_resource.c:1138
msgid "Must specify -t with resource type"
msgstr "需要使用-t指定资源类型"
-#: tools/crm_resource.c:1241
+#: tools/crm_resource.c:1144
msgid "Must supply -v with new value"
msgstr "必须使用-v指定新值"
-#: tools/crm_resource.c:1273
+#: tools/crm_resource.c:1176
msgid "Could not create executor connection"
msgstr "无法创建到pacemaker-execd守护进程的连接"
-#: tools/crm_resource.c:1298
+#: tools/crm_resource.c:1201
#, fuzzy, c-format
msgid "Metadata query for %s failed: %s"
msgstr ",查询%s的元数据失败: %s\n"
-#: tools/crm_resource.c:1304
+#: tools/crm_resource.c:1207
#, c-format
msgid "'%s' is not a valid agent specification"
msgstr "'%s' 是一个无效的代理"
-#: tools/crm_resource.c:1317
+#: tools/crm_resource.c:1220
msgid "--resource cannot be used with --class, --agent, and --provider"
msgstr "--resource 不能与 --class, --agent, --provider一起使用"
-#: tools/crm_resource.c:1322
+#: tools/crm_resource.c:1225
msgid ""
"--class, --agent, and --provider can only be used with --validate and --"
"force-*"
msgstr "--class, --agent和--provider只能被用于--validate和--force-*"
-#: tools/crm_resource.c:1331
+#: tools/crm_resource.c:1234
msgid "stonith does not support providers"
msgstr "stonith 不支持提供者"
-#: tools/crm_resource.c:1335
+#: tools/crm_resource.c:1238
#, c-format
msgid "%s is not a known stonith agent"
msgstr "%s 不是一个已知stonith代理"
-#: tools/crm_resource.c:1340
+#: tools/crm_resource.c:1243
#, c-format
msgid "%s:%s:%s is not a known resource"
msgstr "%s:%s:%s 不是一个已知资源"
-#: tools/crm_resource.c:1454
+#: tools/crm_resource.c:1551
#, c-format
msgid "Error creating output format %s: %s"
msgstr "创建输出格式错误 %s:%s"
-#: tools/crm_resource.c:1481
+#: tools/crm_resource.c:1572
msgid "--expired requires --clear or -U"
msgstr "--expired需要和--clear或-U一起使用"
-#: tools/crm_resource.c:1498
+#: tools/crm_resource.c:1589
#, c-format
msgid "Error parsing '%s' as a name=value pair"
msgstr "'%s'解析错误,格式为name=value"
-#: tools/crm_resource.c:1595
+#: tools/crm_resource.c:1688
msgid "Must supply a resource id with -r"
msgstr "必须使用-r指定资源id"
-#: tools/crm_resource.c:1601
+#: tools/crm_resource.c:1694
msgid "Must supply a node name with -N"
msgstr "必须使用-N指定节点名称"
-#: tools/crm_resource.c:1619
+#: tools/crm_resource.c:1708
msgid "Could not create CIB connection"
msgstr "无法创建到CIB的连接"
-#: tools/crm_resource.c:1627
+#: tools/crm_resource.c:1716
#, c-format
msgid "Could not connect to the CIB: %s"
msgstr "不能连接到CIB:%s"
-#: tools/crm_resource.c:1648
+#: tools/crm_resource.c:1739
#, c-format
msgid "Resource '%s' not found"
msgstr "没有发现'%s'资源"
-#: tools/crm_resource.c:1660
+#: tools/crm_resource.c:1751
#, c-format
msgid "Cannot operate on clone resource instance '%s'"
msgstr "不能操作克隆资源实例'%s'"
-#: tools/crm_resource.c:1672
+#: tools/crm_resource.c:1763
#, c-format
msgid "Node '%s' not found"
msgstr "没有发现%s节点"
-#: tools/crm_resource.c:1683
+#: tools/crm_resource.c:1774
#, c-format
msgid "Error connecting to the controller: %s"
msgstr "连接到控制器错误:%s"
-#: tools/crm_resource.c:1692
+#: tools/crm_resource.c:1783
#, fuzzy, c-format
msgid "Error connecting to %s: %s"
msgstr "连接到控制器错误:%s"
-#: tools/crm_resource.c:1950
+#: tools/crm_resource.c:2052
msgid "You need to supply a value with the -v option"
msgstr "需要使用-v选项提供一个值"
-#: tools/crm_resource.c:2006
+#: tools/crm_resource.c:2106
msgid "You need to specify a resource type with -t"
msgstr "需要使用-t指定资源类型"
-#: tools/crm_resource.c:2013
+#: tools/crm_resource.c:2113
#, fuzzy, c-format
msgid "Could not delete resource %s: %s"
msgstr "无法删除资源:%s:%s"
-#: tools/crm_resource.c:2023
+#: tools/crm_resource.c:2123
#, c-format
msgid "Unimplemented command: %d"
msgstr "无效的命令:%d"
-#: tools/crm_resource.c:2053
+#: tools/crm_resource.c:2153
#, c-format
msgid "Error performing operation: %s"
msgstr "执行操作错误:%s"
+#, fuzzy
+#~ msgid "For example, \"node1,node2,node3\"."
+#~ msgstr "例如, \"node1,node2,node3\"."
+
+#, fuzzy
+#~ msgid "*** Advanced Use Only ***"
+#~ msgstr "*** Advanced Use Only(仅限高级用户使用) ***"
+
+#, fuzzy
+#~ msgid ""
+#~ "Zero disables polling, while positive values are an interval in seconds "
+#~ "(unless other units are specified, for example \"5min\")"
+#~ msgstr ""
+#~ "0 表示禁用轮询,而正值表示以秒为单位的时间间隔(除非指定了其他单位, 例如 "
+#~ "\"5min\" 表示5分钟)"
+
+#~ msgid " Allowed values: "
+#~ msgstr " 允许的值: "
+
+#~ msgid ""
+#~ "This value is not used by Pacemaker, but is kept for backward "
+#~ "compatibility, and certain legacy fence agents might use it."
+#~ msgstr ""
+#~ "Pacemaker不使用此值,但保留此值是为了向后兼容,某些传统的fence 代理可能会"
+#~ "使用它。"
+
#~ msgid "No agents found for standard '%s'"
#~ msgstr "没有发现指定的'%s'标准代理"
diff --git a/python/Makefile.am b/python/Makefile.am
index 803fb0c..71e587e 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2023 the Pacemaker project contributors
+# Copyright 2023-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -24,3 +24,13 @@ check-local:
.PHONY: pylint
pylint:
pylint $(SUBDIRS)
+
+# Disabled warnings:
+# E501 - Line too long
+#
+# Disable unused imports on __init__.py files (we likely just have them
+# there for re-exporting).
+# Disable docstrings warnings on unit tests.
+.PHONY: pyflake
+pyflake:
+ flake8 --ignore=E501 --per-file-ignores="__init__.py:F401 tests/*:D100,D101,D102,D104" $(SUBDIRS)
diff --git a/python/pacemaker/__init__.py b/python/pacemaker/__init__.py
index e5d992e..e6b1b2a 100644
--- a/python/pacemaker/__init__.py
+++ b/python/pacemaker/__init__.py
@@ -1,8 +1,6 @@
-"""
-API reference documentation for the `pacemaker` package.
-"""
+"""API reference documentation for the `pacemaker` package."""
-__copyright__ = "Copyright 2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2023-2024 the Pacemaker project contributors"
__license__ = "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)"
from pacemaker.buildoptions import BuildOptions
diff --git a/python/pacemaker/_cts/CTS.py b/python/pacemaker/_cts/CTS.py
index 166ea10..bc46525 100644
--- a/python/pacemaker/_cts/CTS.py
+++ b/python/pacemaker/_cts/CTS.py
@@ -1,7 +1,7 @@
-""" Main classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Main classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["CtsLab", "NodeStatus", "Process"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import sys
@@ -14,53 +14,52 @@ from pacemaker._cts.input import should_continue
from pacemaker._cts.logging import LogFactory
from pacemaker._cts.remote import RemoteFactory
+
class CtsLab:
- """ A class that defines the Lab Environment for the Cluster Test System.
- It defines those things which are expected to change from test
- environment to test environment for the same cluster manager.
-
- This is where you define the set of nodes that are in your test lab,
- what kind of reset mechanism you use, etc. All this data is stored
- as key/value pairs in an Environment instance constructed from arguments
- passed to this class.
-
- The CTS code ignores names it doesn't know about or need. Individual
- tests have access to this information, and it is perfectly acceptable
- to provide hints, tweaks, fine-tuning directions, or other information
- to the tests through this mechanism.
"""
+ A class that defines the Lab Environment for the Cluster Test System.
- def __init__(self, args=None):
- """ Create a new CtsLab instance. This class can be treated kind
- of like a dictionary due to the presence of typical dict functions
- like __contains__, __getitem__, and __setitem__. However, it is not a
- dictionary so do not rely on standard dictionary behavior.
+ It defines those things which are expected to change from test
+ environment to test environment for the same cluster manager.
- Arguments:
+ This is where you define the set of nodes that are in your test lab,
+ what kind of reset mechanism you use, etc. All this data is stored
+ as key/value pairs in an Environment instance constructed from arguments
+ passed to this class.
+
+ The CTS code ignores names it doesn't know about or need. Individual
+ tests have access to this information, and it is perfectly acceptable
+ to provide hints, tweaks, fine-tuning directions, or other information
+ to the tests through this mechanism.
+ """
- args -- A list of command line parameters, minus the program name.
+ def __init__(self, args=None):
"""
+ Create a new CtsLab instance.
+
+ This class can be treated kind of like a dictionary due to the presence
+ of typical dict functions like __contains__, __getitem__, and __setitem__.
+ However, it is not a dictionary so do not rely on standard dictionary
+ behavior.
+ Arguments:
+ args -- A list of command line parameters, minus the program name.
+ """
self._env = EnvFactory().getInstance(args)
self._logger = LogFactory()
def dump(self):
- """ Print the current environment """
-
+ """Print the current environment."""
self._env.dump()
def __contains__(self, key):
- """ Does the given environment key exist? """
-
+ """Return True if the given environment key exists."""
# pylint gets confused because of EnvFactory here.
# pylint: disable=unsupported-membership-test
return key in self._env
def __getitem__(self, key):
- """ Return the given environment key, or raise KeyError if it does
- not exist
- """
-
+ """Return the given environment key, or raise KeyError if it does not exist."""
# Throughout this file, pylint has trouble understanding that EnvFactory
# and RemoteFactory are singleton instances that can be treated as callable
# and subscriptable objects. Various warnings are disabled because of this.
@@ -69,21 +68,16 @@ class CtsLab:
return self._env[key]
def __setitem__(self, key, value):
- """ Set the given environment key to the given value, overriding any
- previous value
- """
-
+ """Set the given environment key to the given value, overriding any previous value."""
# pylint: disable=unsupported-assignment-operation
self._env[key] = value
def run(self, scenario, iterations):
- """ Run the given scenario the given number of times.
-
- Returns:
-
- ExitStatus.OK on success, or ExitStatus.ERROR on error
"""
+ Run the given scenario the given number of times.
+ Returns ExitStatus.OK on success, or ExitStatus.ERROR on error.
+ """
if not scenario:
self._logger.log("No scenario was defined")
return ExitStatus.ERROR
@@ -101,7 +95,7 @@ class CtsLab:
# pylint: disable=bare-except
try:
scenario.run(iterations)
- except:
+ except: # noqa: E722
self._logger.log("Exception by %s" % sys.exc_info()[0])
self._logger.traceback(traceback)
@@ -123,43 +117,43 @@ class CtsLab:
class NodeStatus:
- """ A class for querying the status of cluster nodes - are nodes up? Do
- they respond to SSH connections?
"""
+ A class for querying the status of cluster nodes.
- def __init__(self, env):
- """ Create a new NodeStatus instance
+ Are nodes up? Do they respond to SSH connections?
+ """
- Arguments:
+ def __init__(self, env):
+ """
+ Create a new NodeStatus instance.
- env -- An Environment instance
+ Arguments:
+ env -- An Environment instance
"""
self._env = env
def _node_booted(self, node):
- """ Return True if the given node is booted (responds to pings) """
-
+ """Return True if the given node is booted (responds to pings)."""
# pylint: disable=not-callable
(rc, _) = RemoteFactory().getInstance()("localhost", "ping -nq -c1 -w1 %s" % node, verbose=0)
return rc == 0
def _sshd_up(self, node):
- """ Return true if sshd responds on the given node """
-
+ """Return true if sshd responds on the given node."""
# pylint: disable=not-callable
(rc, _) = RemoteFactory().getInstance()(node, "true", verbose=0)
return rc == 0
def wait_for_node(self, node, timeout=300):
- """ Wait for a node to become available. Should the timeout be reached,
- the user will be given a choice whether to continue or not. If not,
- ValueError will be raised.
+ """
+ Wait for a node to become available.
- Returns:
+ Should the timeout be reached, the user will be given a choice whether
+ to continue or not. If not, ValueError will be raised.
- True when the node is available, or False if the timeout is reached.
+ Returns True when the node is available, or False if the timeout is
+ reached.
"""
-
initial_timeout = timeout
anytimeouts = False
@@ -186,8 +180,7 @@ class NodeStatus:
return False
def wait_for_all_nodes(self, nodes, timeout=300):
- """ Return True when all nodes come up, or False if the timeout is reached """
-
+ """Return True when all nodes come up, or False if the timeout is reached."""
for node in nodes:
if not self.wait_for_node(node, timeout):
return False
@@ -196,24 +189,23 @@ class NodeStatus:
class Process:
- """ A class for managing a Pacemaker daemon """
+ """A class for managing a Pacemaker daemon."""
# pylint: disable=invalid-name
def __init__(self, cm, name, dc_only=False, pats=None, dc_pats=None,
badnews_ignore=None):
- """ Create a new Process instance.
-
- Arguments:
-
- cm -- A ClusterManager instance
- name -- The command being run
- dc_only -- Should this daemon be killed only on the DC?
- pats -- Regexes we expect to find in log files
- dc_pats -- Additional DC-specific regexes we expect to find
- in log files
- badnews_ignore -- Regexes for lines in the log that can be ignored
"""
-
+ Create a new Process instance.
+
+ Arguments:
+ cm -- A ClusterManager instance
+ name -- The command being run
+ dc_only -- Should this daemon be killed only on the DC?
+ pats -- Regexes we expect to find in log files
+ dc_pats -- Additional DC-specific regexes we expect to find
+ in log files
+ badnews_ignore -- Regexes for lines in the log that can be ignored
+ """
self._cm = cm
self.badnews_ignore = badnews_ignore
self.dc_only = dc_only
@@ -231,8 +223,7 @@ class Process:
self.pats = []
def kill(self, node):
- """ Kill the instance of this process running on the given node """
-
+ """Kill the instance of this process running on the given node."""
(rc, _) = self._cm.rsh(node, "killall -9 %s" % self.name)
if rc != 0:
diff --git a/python/pacemaker/_cts/__init__.py b/python/pacemaker/_cts/__init__.py
index dfc05ad..5b8dfab 100644
--- a/python/pacemaker/_cts/__init__.py
+++ b/python/pacemaker/_cts/__init__.py
@@ -1,6 +1,4 @@
-"""
-Internal Python API for the `pacemaker` package.
-"""
+"""Internal Python API for the `pacemaker` package."""
-__copyright__ = "Copyright 2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2023-2024 the Pacemaker project contributors"
__license__ = "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)"
diff --git a/python/pacemaker/_cts/audits.py b/python/pacemaker/_cts/audits.py
index dc66f96..74f8b18 100644
--- a/python/pacemaker/_cts/audits.py
+++ b/python/pacemaker/_cts/audits.py
@@ -1,7 +1,7 @@
-""" Auditing classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Auditing classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["AuditConstraint", "AuditResource", "ClusterAudit", "audit_list"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -14,65 +14,67 @@ from pacemaker._cts.watcher import LogKind, LogWatcher
class ClusterAudit:
- """ The base class for various kinds of auditors. Specific audit implementations
- should be built on top of this one. Audits can do all kinds of checks on the
- system. The basic interface for callers is the `__call__` method, which
- returns True if the audit passes and False if it fails.
"""
+ The base class for various kinds of auditors.
- def __init__(self, cm):
- """ Create a new ClusterAudit instance
-
- Arguments:
+ Specific audit implementations should be built on top of this one. Audits
+ can do all kinds of checks on the system. The basic interface for callers
+ is the `__call__` method, which returns True if the audit passes and False
+ if it fails.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new ClusterAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
# pylint: disable=invalid-name
self._cm = cm
self.name = None
def __call__(self):
+ """Perform the audit action."""
raise NotImplementedError
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration.
- This method must be implemented by all subclasses.
"""
+ Return True if this audit is applicable in the current test configuration.
+ This method must be implemented by all subclasses.
+ """
raise NotImplementedError
def log(self, args):
- """ Log a message """
-
+ """Log a message."""
self._cm.log("audit: %s" % args)
def debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
self._cm.debug("audit: %s" % args)
class LogAudit(ClusterAudit):
- """ Audit each cluster node to verify that some logging system is usable.
- This is done by logging a unique test message and then verifying that
- we can read back that test message using logging tools.
"""
+ Audit each cluster node to verify that some logging system is usable.
- def __init__(self, cm):
- """ Create a new LogAudit instance
-
- Arguments:
+ This is done by logging a unique test message and then verifying that we
+ can read back that test message using logging tools.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new LogAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "LogAudit"
def _restart_cluster_logging(self, nodes=None):
- """ Restart logging on the given nodes, or all if none are given """
-
+ """Restart logging on the given nodes, or all if none are given."""
if not nodes:
nodes = self._cm.env["nodes"]
@@ -93,8 +95,7 @@ class LogAudit(ClusterAudit):
self._cm.log("ERROR: Cannot restart '%s' on %s" % (self._cm.env["syslogd"], node))
def _create_watcher(self, patterns, kind):
- """ Create a new LogWatcher instance for the given patterns """
-
+ """Create a new LogWatcher instance for the given patterns."""
watch = LogWatcher(self._cm.env["LogFileName"], patterns,
self._cm.env["nodes"], kind, "LogAudit", 5,
silent=True)
@@ -102,8 +103,7 @@ class LogAudit(ClusterAudit):
return watch
def _test_logging(self):
- """ Perform the log audit """
-
+ """Perform the log audit."""
patterns = []
prefix = "Test message from"
suffix = str(uuid.uuid4())
@@ -120,12 +120,13 @@ class LogAudit(ClusterAudit):
patterns.append("%s.*%s %s %s" % (simple, prefix, node, suffix))
- watch_pref = self._cm.env["LogWatcher"]
- if watch_pref == LogKind.ANY:
- kinds = [LogKind.FILE]
+ watch_pref = self._cm.env["log_kind"]
+ if watch_pref is None:
+ kinds = [LogKind.LOCAL_FILE]
if self._cm.env["have_systemd"]:
- kinds += [LogKind.JOURNAL]
- kinds += [LogKind.REMOTE_FILE]
+ kinds.append(LogKind.JOURNAL)
+ kinds.append(LogKind.REMOTE_FILE)
+
for k in kinds:
watch[k] = self._create_watcher(patterns, k)
self._cm.log("Logging test message with identifier %s" % suffix)
@@ -141,21 +142,22 @@ class LogAudit(ClusterAudit):
for k in list(watch.keys()):
w = watch[k]
- if watch_pref == LogKind.ANY:
+ if watch_pref is None:
self._cm.log("Checking for test message in %s logs" % k)
w.look_for_all(silent=True)
if w.unmatched:
for regex in w.unmatched:
self._cm.log("Test message [%s] not found in %s logs" % (regex, w.kind))
else:
- if watch_pref == LogKind.ANY:
+ if watch_pref is None:
self._cm.log("Found test message in %s logs" % k)
- self._cm.env["LogWatcher"] = k
+ self._cm.env["log_kind"] = k
return 1
return False
def __call__(self):
+ """Perform the audit action."""
max_attempts = 3
attempt = 0
@@ -163,7 +165,7 @@ class LogAudit(ClusterAudit):
while attempt <= max_attempts and not self._test_logging():
attempt += 1
self._restart_cluster_logging()
- time.sleep(60*attempt)
+ time.sleep(60 * attempt)
if attempt > max_attempts:
self._cm.log("ERROR: Cluster logging unrecoverable.")
@@ -172,8 +174,7 @@ class LogAudit(ClusterAudit):
return True
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
if self._cm.env["LogAuditDisabled"]:
return False
@@ -181,25 +182,28 @@ class LogAudit(ClusterAudit):
class DiskAudit(ClusterAudit):
- """ Audit disk usage on cluster nodes to verify that there is enough free
- space left on whichever mounted file system holds the logs.
-
- Warn on: less than 100 MB or 10% of free space
- Error on: less than 10 MB or 5% of free space
"""
+ Audit disk usage on cluster nodes.
- def __init__(self, cm):
- """ Create a new DiskAudit instance
+ Verify that there is enough free space left on whichever mounted file
+ system holds the logs.
- Arguments:
+ Warn on: less than 100 MB or 10% of free space
+ Error on: less than 10 MB or 5% of free space
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new DiskAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "DiskspaceAudit"
def __call__(self):
+ """Perform the audit action."""
result = True
# @TODO Use directory of PCMK_logfile if set on host
@@ -236,31 +240,32 @@ class DiskAudit(ClusterAudit):
return result
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
return True
class FileAudit(ClusterAudit):
- """ Audit the filesystem looking for various failure conditions:
+ """
+ Audit the filesystem looking for various failure conditions.
- * The presence of core dumps from corosync or Pacemaker daemons
- * Stale IPC files
+ Check for:
+ * The presence of core dumps from corosync or Pacemaker daemons
+ * Stale IPC files
"""
def __init__(self, cm):
- """ Create a new FileAudit instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new FileAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.known = []
self.name = "FileAudit"
def __call__(self):
+ """Perform the audit action."""
result = True
self._cm.ns.wait_for_all_nodes(self._cm.env["nodes"])
@@ -307,24 +312,22 @@ class FileAudit(ClusterAudit):
return result
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
return True
class AuditResource:
- """ A base class for storing information about a cluster resource """
+ """A base class for storing information about a cluster resource."""
def __init__(self, cm, line):
- """ Create a new AuditResource instance
-
- Arguments:
-
- cm -- A ClusterManager instance
- line -- One line of output from `crm_resource` describing a single
- resource
"""
+ Create a new AuditResource instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ line -- One line of output from `crm_resource` describing a single
+ resource
+ """
# pylint: disable=invalid-name
fields = line.split()
self._cm = cm
@@ -346,36 +349,32 @@ class AuditResource:
@property
def unique(self):
- """ Is this resource unique? """
-
+ """Return True if this resource is unique."""
return self.flags & 0x20
@property
def orphan(self):
- """ Is this resource an orphan? """
-
+ """Return True if this resource is an orphan."""
return self.flags & 0x01
@property
def managed(self):
- """ Is this resource managed by the cluster? """
-
+ """Return True if this resource is managed by the cluster."""
return self.flags & 0x02
class AuditConstraint:
- """ A base class for storing information about a cluster constraint """
+ """A base class for storing information about a cluster constraint."""
def __init__(self, cm, line):
- """ Create a new AuditConstraint instance
-
- Arguments:
-
- cm -- A ClusterManager instance
- line -- One line of output from `crm_resource` describing a single
- constraint
"""
+ Create a new AuditConstraint instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ line -- One line of output from `crm_resource` describing a single
+ constraint
+ """
# pylint: disable=invalid-name
fields = line.split()
self._cm = cm
@@ -396,19 +395,22 @@ class AuditConstraint:
class PrimitiveAudit(ClusterAudit):
- """ Audit primitive resources to verify a variety of conditions, including that
- they are active and managed only when expected; they are active on the
- expected clusted node; and that they are not orphaned.
"""
+ Audit primitive resources to verify a variety of conditions.
- def __init__(self, cm):
- """ Create a new PrimitiveAudit instance
-
- Arguments:
+ Check that:
+ * Resources are active and managed only when expected
+ * Resources are active on the expected cluster node
+ * Resources are not orphaned
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new PrimitiveAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "PrimitiveAudit"
@@ -419,8 +421,7 @@ class PrimitiveAudit(ClusterAudit):
self._target = None
def _audit_resource(self, resource, quorum):
- """ Perform the audit of a single resource """
-
+ """Perform the audit of a single resource."""
rc = True
active = self._cm.resource_location(resource.id)
@@ -468,10 +469,11 @@ class PrimitiveAudit(ClusterAudit):
return rc
def _setup(self):
- """ Verify cluster nodes are active, and collect resource and colocation
- information used for performing the audit.
"""
+ Verify cluster nodes are active.
+ Collect resource and colocation information used for performing the audit.
+ """
for node in self._cm.env["nodes"]:
if self._cm.expected_status[node] == "up":
self._active_nodes.append(node)
@@ -501,6 +503,7 @@ class PrimitiveAudit(ClusterAudit):
return True
def __call__(self):
+ """Perform the audit action."""
result = True
if not self._setup():
@@ -514,30 +517,31 @@ class PrimitiveAudit(ClusterAudit):
return result
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
# @TODO Due to long-ago refactoring, this name test would never match,
# so this audit (and those derived from it) would never run.
# Uncommenting the next lines fixes the name test, but that then
# exposes pre-existing bugs that need to be fixed.
- #if self._cm["Name"] == "crm-corosync":
- # return True
+ # if self._cm["Name"] == "crm-corosync":
+ # return True
return False
class GroupAudit(PrimitiveAudit):
- """ Audit group resources to verify that each of its child primitive
- resources is active on the expected cluster node.
"""
+ Audit group resources.
- def __init__(self, cm):
- """ Create a new GroupAudit instance
-
- Arguments:
+ Check that:
+ * Each of its child primitive resources is active on the expected cluster node
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new GroupAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
PrimitiveAudit.__init__(self, cm)
self.name = "GroupAudit"
@@ -587,18 +591,19 @@ class GroupAudit(PrimitiveAudit):
class CloneAudit(PrimitiveAudit):
- """ Audit clone resources. NOTE: Currently, this class does not perform
- any actual audit functions.
"""
+ Audit clone resources.
- def __init__(self, cm):
- """ Create a new CloneAudit instance
-
- Arguments:
+ NOTE: Currently, this class does not perform any actual audit functions.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new CloneAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
PrimitiveAudit.__init__(self, cm)
self.name = "CloneAudit"
@@ -624,24 +629,26 @@ class CloneAudit(PrimitiveAudit):
class ColocationAudit(PrimitiveAudit):
- """ Audit cluster resources to verify that those that should be colocated
- with each other actually are.
"""
+ Audit cluster resources.
- def __init__(self, cm):
- """ Create a new ColocationAudit instance
+ Check that:
- Arguments:
+ * Resources are colocated with the expected resource
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new ColocationAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
PrimitiveAudit.__init__(self, cm)
self.name = "ColocationAudit"
def _crm_location(self, resource):
- """ Return a list of cluster nodes where a given resource is running """
-
+ """Return a list of cluster nodes where a given resource is running."""
(rc, lines) = self._cm.rsh(self._target, "crm_resource -W -r %s -Q" % resource, verbose=1)
hosts = []
@@ -669,7 +676,7 @@ class ColocationAudit(PrimitiveAudit):
self.debug("Colocation audit (%s): %s not running" % (coloc.id, coloc.rsc))
else:
for node in source:
- if not node in target:
+ if node not in target:
result = False
self._cm.log("Colocation audit (%s): %s running on %s (not in %r)"
% (coloc.id, coloc.rsc, node, target))
@@ -681,18 +688,15 @@ class ColocationAudit(PrimitiveAudit):
class ControllerStateAudit(ClusterAudit):
- """ Audit cluster nodes to verify that those we expect to be active are
- active, and those that are expected to be inactive are inactive.
- """
+ """Verify active and inactive resources."""
def __init__(self, cm):
- """ Create a new ControllerStateAudit instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new ControllerStateAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "ControllerStateAudit"
@@ -734,28 +738,26 @@ class ControllerStateAudit(ClusterAudit):
return result
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
# @TODO Due to long-ago refactoring, this name test would never match,
# so this audit (and those derived from it) would never run.
# Uncommenting the next lines fixes the name test, but that then
# exposes pre-existing bugs that need to be fixed.
- #if self._cm["Name"] == "crm-corosync":
- # return True
+ # if self._cm["Name"] == "crm-corosync":
+ # return True
return False
class CIBAudit(ClusterAudit):
- """ Audit the CIB by verifying that it is identical across cluster nodes """
+ """Audit the CIB by verifying that it is identical across cluster nodes."""
def __init__(self, cm):
- """ Create a new CIBAudit instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new CIBAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "CibAudit"
@@ -776,8 +778,7 @@ class CIBAudit(ClusterAudit):
return result
def _audit_cib_contents(self, hostlist):
- """ Perform the CIB audit on the given hosts """
-
+ """Perform the CIB audit on the given hosts."""
passed = True
node0 = None
node0_xml = None
@@ -816,10 +817,11 @@ class CIBAudit(ClusterAudit):
return passed
def _store_remote_cib(self, node, target):
- """ Store a copy of the given node's CIB on the given target node. If
- no target is given, store the CIB on the given node.
"""
+ Store a copy of the given node's CIB on the given target node.
+ If no target is given, store the CIB on the given node.
+ """
filename = "/tmp/ctsaudit.%s.xml" % node
if not target:
@@ -841,36 +843,37 @@ class CIBAudit(ClusterAudit):
return filename
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
# @TODO Due to long-ago refactoring, this name test would never match,
# so this audit (and those derived from it) would never run.
# Uncommenting the next lines fixes the name test, but that then
# exposes pre-existing bugs that need to be fixed.
- #if self._cm["Name"] == "crm-corosync":
- # return True
+ # if self._cm["Name"] == "crm-corosync":
+ # return True
return False
class PartitionAudit(ClusterAudit):
- """ Audit each partition in a cluster to verify a variety of conditions:
-
- * The number of partitions and the nodes in each is as expected
- * Each node is active when it should be active and inactive when it
- should be inactive
- * The status and epoch of each node is as expected
- * A partition has quorum
- * A partition has a DC when expected
"""
+ Audit each partition in a cluster to verify a variety of conditions.
- def __init__(self, cm):
- """ Create a new PartitionAudit instance
+ Check that:
- Arguments:
+ * The number of partitions and the nodes in each is as expected
+ * Each node is active when it should be active and inactive when it
+ should be inactive
+ * The status and epoch of each node is as expected
+ * A partition has quorum
+ * A partition has a DC when expected
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new PartitionAudit instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
ClusterAudit.__init__(self, cm)
self.name = "PartitionAudit"
@@ -901,8 +904,7 @@ class PartitionAudit(ClusterAudit):
return result
def _trim_string(self, avalue):
- """ Remove the last character from a multi-character string """
-
+ """Remove the last character from a multi-character string."""
if not avalue:
return None
@@ -912,10 +914,7 @@ class PartitionAudit(ClusterAudit):
return avalue
def _trim2int(self, avalue):
- """ Remove the last character from a multi-character string and convert
- the result to an int.
- """
-
+ """Remove the last character from a multi-character string and convert the result to an int."""
trimmed = self._trim_string(avalue)
if trimmed:
return int(trimmed)
@@ -923,8 +922,7 @@ class PartitionAudit(ClusterAudit):
return None
def _audit_partition(self, partition):
- """ Perform the audit of a single partition """
-
+ """Perform the audit of a single partition."""
passed = True
dc_found = []
dc_allowed_list = []
@@ -1000,23 +998,19 @@ class PartitionAudit(ClusterAudit):
return passed
def is_applicable(self):
- """ Return True if this audit is applicable in the current test configuration. """
-
+ """Return True if this audit is applicable in the current test configuration."""
# @TODO Due to long-ago refactoring, this name test would never match,
# so this audit (and those derived from it) would never run.
# Uncommenting the next lines fixes the name test, but that then
# exposes pre-existing bugs that need to be fixed.
- #if self._cm["Name"] == "crm-corosync":
- # return True
+ # if self._cm["Name"] == "crm-corosync":
+ # return True
return False
# pylint: disable=invalid-name
def audit_list(cm):
- """ Return a list of instances of applicable audits that can be performed
- for the given ClusterManager.
- """
-
+ """Return a list of instances of applicable audits that can be performed."""
result = []
for auditclass in [DiskAudit, FileAudit, LogAudit, ControllerStateAudit,
diff --git a/python/pacemaker/_cts/cib.py b/python/pacemaker/_cts/cib.py
index b8b5d5d..bb33077 100644
--- a/python/pacemaker/_cts/cib.py
+++ b/python/pacemaker/_cts/cib.py
@@ -1,7 +1,7 @@
-""" CIB generator for Pacemaker's Cluster Test Suite (CTS) """
+"""CIB generator for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["ConfigFactory"]
-__copyright__ = "Copyright 2008-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2008-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import warnings
@@ -13,21 +13,18 @@ from pacemaker._cts.network import next_ip
class CIB:
- """ A class for generating, representing, and installing a CIB file onto
- cluster nodes
- """
+ """A class for generating, representing, and installing a CIB file onto cluster nodes."""
def __init__(self, cm, version, factory, tmpfile=None):
- """ Create a new CIB instance
-
- Arguments:
-
- cm -- A ClusterManager instance
- version -- The schema syntax version
- factory -- A ConfigFactory instance
- tmpfile -- Where to store the CIB, or None to use a new tempfile
"""
+ Create a new CIB instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ version -- The schema syntax version
+ factory -- A ConfigFactory instance
+ tmpfile -- Where to store the CIB, or None to use a new tempfile
+ """
# pylint: disable=invalid-name
self._cib = None
self._cm = cm
@@ -50,8 +47,7 @@ class CIB:
self._factory.tmpfile = tmpfile
def _show(self):
- """ Query a cluster node for its generated CIB; log and return the result """
-
+ """Query a cluster node for its generated CIB; log and return the result."""
output = ""
(_, result) = self._factory.rsh(self._factory.target, "HOME=/root CIB_file=%s cibadmin -Ql" % self._factory.tmpfile, verbose=1)
@@ -62,10 +58,7 @@ class CIB:
return output
def new_ip(self, name=None):
- """ Generate an IP resource for the next available IP address, optionally
- specifying the resource's name.
- """
-
+ """Generate an IP resource for the next available IP address, optionally specifying the resource's name."""
if self._cm.env["IPagent"] == "IPaddr2":
ip = next_ip(self._cm.env["IPBase"])
if not name:
@@ -95,8 +88,7 @@ class CIB:
return r
def get_node_id(self, node_name):
- """ Check the cluster configuration for the node ID for the given node_name """
-
+ """Check the cluster configuration for the node ID for the given node_name."""
# We can't account for every possible configuration,
# so we only return a node ID if:
# * The node is specified in /etc/corosync/corosync.conf
@@ -125,8 +117,7 @@ class CIB:
return node_id
def install(self, target):
- """ Generate a CIB file and install it to the given cluster node """
-
+ """Generate a CIB file and install it to the given cluster node."""
old = self._factory.tmpfile
# Force a rebuild
@@ -139,8 +130,7 @@ class CIB:
self._factory.tmpfile = old
def contents(self, target):
- """ Generate a complete CIB file """
-
+ """Generate a complete CIB file."""
# fencing resource
if self._cib:
return self._cib
@@ -175,7 +165,7 @@ class CIB:
# For remote node tests, a cluster node is stopped and brought back up
# as a remote node with the name "remote-OLDNAME". To allow fencing
# devices to fence these nodes, create a list of all possible node names.
- all_node_names = [prefix+n for n in self._cm.env["nodes"] for prefix in ('', 'remote-')]
+ all_node_names = [prefix + n for n in self._cm.env["nodes"] for prefix in ('', 'remote-')]
# Add all parameters specified by user
entries = self._cm.env["stonith-params"].split(',')
@@ -313,8 +303,7 @@ class CIB:
return self._cib
def add_resources(self):
- """ Add various resources and their constraints to the CIB """
-
+ """Add various resources and their constraints to the CIB."""
# Per-node resources
for node in self._cm.env["nodes"]:
name = "rsc_%s" % node
@@ -392,16 +381,15 @@ class CIB:
class ConfigFactory:
- """ Singleton to generate a CIB file for the environment's schema version """
+ """Singleton to generate a CIB file for the environment's schema version."""
def __init__(self, cm):
- """ Create a new ConfigFactory instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new ConfigFactory instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
# pylint: disable=invalid-name
self._cm = cm
self.rsh = self._cm.rsh
@@ -410,16 +398,13 @@ class ConfigFactory:
self.tmpfile = None
def log(self, args):
- """ Log a message """
-
+ """Log a message."""
self._cm.log("cib: %s" % args)
def debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
self._cm.debug("cib: %s" % args)
def create_config(self, name="pacemaker-%s" % BuildOptions.CIB_SCHEMA_VERSION):
- """ Return a CIB object for the given schema version """
-
+ """Return a CIB object for the given schema version."""
return CIB(self._cm, name, self)
diff --git a/python/pacemaker/_cts/cibxml.py b/python/pacemaker/_cts/cibxml.py
index 52e3721..a5bc315 100644
--- a/python/pacemaker/_cts/cibxml.py
+++ b/python/pacemaker/_cts/cibxml.py
@@ -1,4 +1,4 @@
-""" CIB XML generator for Pacemaker's Cluster Test Suite (CTS) """
+"""CIB XML generator for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = [
"Alerts",
@@ -12,23 +12,22 @@ __all__ = [
"Resource",
"Rule",
]
-__copyright__ = "Copyright 2008-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2008-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
def key_val_string(**kwargs):
- """ Given keyword arguments as key=value pairs, construct a single string
- containing all those pairs separated by spaces. This is suitable for
- using in an XML element as a list of its attributes.
+ """
+ Construct a string from kwargs containing key=value pairs separated by spaces.
- Any pairs that have value=None will be skipped.
+ This is suitable for using in an XML element as a list of its attributes.
+ Any pairs that have value=None will be skipped.
- Note that a dictionary can be passed to this function instead of kwargs
- by using a construction like:
+ Note that a dictionary can be passed to this function instead of kwargs
+ by using a construction like:
key_val_string(**{"a": 1, "b": 2})
"""
-
retval = ""
for (k, v) in kwargs.items():
@@ -41,47 +40,46 @@ def key_val_string(**kwargs):
def element(element_name, **kwargs):
- """ Create an XML element string with the given element_name and attributes.
- This element does not support having any children, so it will be closed
- on the same line. The attributes are processed by key_val_string.
"""
+ Create an XML element string with the given element_name and attributes.
+ This element does not support having any children, so it will be closed
+ on the same line. The attributes are processed by key_val_string.
+ """
return "<%s %s/>" % (element_name, key_val_string(**kwargs))
def containing_element(element_name, inner, **kwargs):
- """ Like element, but surrounds some child text passed by the inner
- parameter.
- """
-
+ """Like element, but surrounds some child text passed by the inner parameter."""
attrs = key_val_string(**kwargs)
return "<%s %s>%s</%s>" % (element_name, attrs, inner, element_name)
class XmlBase:
- """ A base class for deriving all kinds of XML sections in the CIB. This
- class contains only the most basic operations common to all sections.
- It is up to subclasses to provide most behavior.
-
- Note that subclasses of this base class often have different sets of
- arguments to their __init__ methods. In general this is not a great
- practice, however it is so thoroughly used in these classes that trying
- to straighten it out is likely to cause more bugs than just leaving it
- alone for now.
"""
+ A base class for deriving all kinds of XML sections in the CIB.
- def __init__(self, factory, tag, _id, **kwargs):
- """ Create a new XmlBase instance
+ This class contains only the most basic operations common to all sections.
+ It is up to subclasses to provide most behavior.
- Arguments:
+ Note that subclasses of this base class often have different sets of
+ arguments to their __init__ methods. In general this is not a great
+ practice, however it is so thoroughly used in these classes that trying
+ to straighten it out is likely to cause more bugs than just leaving it
+ alone for now.
+ """
- factory -- A ConfigFactory instance
- tag -- The XML element's start and end tag
- _id -- A unique name for the element
- kwargs -- Any additional key/value pairs that should be added to
- this element as attributes
+ def __init__(self, factory, tag, _id, **kwargs):
+ """
+ Create a new XmlBase instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ tag -- The XML element's start and end tag
+ _id -- A unique name for the element
+ kwargs -- Any additional key/value pairs that should be added to
+ this element as attributes
"""
-
self._children = []
self._factory = factory
self._kwargs = kwargs
@@ -90,30 +88,27 @@ class XmlBase:
self.name = _id
def __repr__(self):
- """ Return a short string description of this XML section """
-
+ """Return a short string description of this XML section."""
return "%s-%s" % (self._tag, self.name)
def add_child(self, child):
- """ Add an XML section as a child of this one """
-
+ """Add an XML section as a child of this one."""
self._children.append(child)
def __setitem__(self, key, value):
- """ Add a key/value pair to this element, resulting in it becoming an
- XML attribute. If value is None, remove the key.
"""
+ Add a key/value pair to this element.
+ The resulting pair becomes an XML attribute. If value is None, remove
+ the key.
+ """
if value:
self._kwargs[key] = value
else:
self._kwargs.pop(key, None)
def show(self):
- """ Return a string representation of this XML section, including all
- of its children
- """
-
+ """Recursively return a string representation of this XML section."""
text = '''<%s''' % self._tag
if self.name:
text += ''' id="%s"''' % self.name
@@ -133,19 +128,17 @@ class XmlBase:
return text
def _run(self, operation, xml, section, options=""):
- """ Update the CIB on the cluster to include this XML section, including
- all of its children
-
- Arguments:
-
- operation -- Whether this update is a "create" or "modify" operation
- xml -- The XML to update the CIB with, typically the result
- of calling show
- section -- Which section of the CIB this update applies to (see
- the --scope argument to cibadmin for allowed values)
- options -- Extra options to pass to cibadmin
"""
-
+ Update the CIB on the cluster to include this XML section.
+
+ Arguments:
+ operation -- Whether this update is a "create" or "modify" operation
+ xml -- The XML to update the CIB with, typically the result
+ of calling show
+ section -- Which section of the CIB this update applies to (see
+ the --scope argument to cibadmin for allowed values)
+ options -- Extra options to pass to cibadmin
+ """
if self.name:
label = self.name
else:
@@ -162,20 +155,17 @@ class XmlBase:
class InstanceAttributes(XmlBase):
- """ A class that creates an <instance_attributes> XML section with
- key/value pairs
- """
+ """Create an <instance_attributes> XML section with key/value pairs."""
def __init__(self, factory, _id, attrs):
- """ Create a new InstanceAttributes instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
- attrs -- Key/value pairs to add as nvpair child elements
"""
+ Create a new InstanceAttributes instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ attrs -- Key/value pairs to add as nvpair child elements
+ """
XmlBase.__init__(self, factory, "instance_attributes", _id)
# Create an <nvpair> for each attribute
@@ -185,90 +175,79 @@ class InstanceAttributes(XmlBase):
class Node(XmlBase):
- """ A class that creates a <node> XML section for a single node, complete
- with node attributes
- """
+ """Create a <node> XML section for a single node, complete with node attributes."""
def __init__(self, factory, node_name, node_id, node_attrs):
- """ Create a new Node instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
- node_name -- The value of the uname attribute for this node
- node_id -- A unique name for the element
- node_attrs -- Additional key/value pairs to set as instance
- attributes for this node
"""
-
+ Create a new Node instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ node_name -- The value of the uname attribute for this node
+ node_id -- A unique name for the element
+ node_attrs -- Additional key/value pairs to set as instance
+ attributes for this node
+ """
XmlBase.__init__(self, factory, "node", node_id, uname=node_name)
self.add_child(InstanceAttributes(factory, "%s-1" % node_name, node_attrs))
class Nodes(XmlBase):
- """ A class that creates a <nodes> XML section containing multiple Node
- instances as children
- """
+ """Create a <nodes> XML section containing multiple Node instances as children."""
def __init__(self, factory):
- """ Create a new Nodes instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
"""
+ Create a new Nodes instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ """
XmlBase.__init__(self, factory, "nodes", None)
def add_node(self, node_name, node_id, node_attrs):
- """ Add a child node element
-
- Arguments:
-
- node_name -- The value of the uname attribute for this node
- node_id -- A unique name for the element
- node_attrs -- Additional key/value pairs to set as instance
- attributes for this node
"""
+ Add a child node element.
+ Arguments:
+ node_name -- The value of the uname attribute for this node
+ node_id -- A unique name for the element
+ node_attrs -- Additional key/value pairs to set as instance
+ attributes for this node
+ """
self.add_child(Node(self._factory, node_name, node_id, node_attrs))
def commit(self):
- """ Modify the CIB on the cluster to include this XML section """
-
+ """Modify the CIB on the cluster to include this XML section."""
self._run("modify", self.show(), "configuration", "--allow-create")
class FencingTopology(XmlBase):
- """ A class that creates a <fencing-topology> XML section describing how
- fencing is configured in the cluster
- """
+ """Create a <fencing-topology> XML section describing how fencing is configured in the cluster."""
def __init__(self, factory):
- """ Create a new FencingTopology instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
"""
+ Create a new FencingTopology instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ """
XmlBase.__init__(self, factory, "fencing-topology", None)
def level(self, index, target, devices, target_attr=None, target_value=None):
- """ Generate a <fencing-level> XML element
-
- index -- The order in which to attempt fencing-levels
- (1 through 9). Levels are attempted in ascending
- order until one succeeds.
- target -- The name of a single node to which this level applies
- devices -- A list of devices that must all be tried for this
- level
- target_attr -- The name of a node attribute that is set for nodes
- to which this level applies
- target_value -- The value of a node attribute that is set for nodes
- to which this level applies
"""
-
+ Generate a <fencing-level> XML element.
+
+ index -- The order in which to attempt fencing-levels
+ (1 through 9). Levels are attempted in ascending
+ order until one succeeds.
+ target -- The name of a single node to which this level applies
+ devices -- A list of devices that must all be tried for this
+ level
+ target_attr -- The name of a node attribute that is set for nodes
+ to which this level applies
+ target_value -- The value of a node attribute that is set for nodes
+ to which this level applies
+ """
if target:
xml_id = "cts-%s.%d" % (target, index)
self.add_child(XmlBase(self._factory, "fencing-level", xml_id, target=target, index=index, devices=devices))
@@ -281,92 +260,77 @@ class FencingTopology(XmlBase):
self.add_child(child)
def commit(self):
- """ Create this XML section in the CIB """
-
+ """Create this XML section in the CIB."""
self._run("create", self.show(), "configuration", "--allow-create")
class Option(XmlBase):
- """ A class that creates a <cluster_property_set> XML section of key/value
- pairs for cluster-wide configuration settings
- """
+ """Create a <cluster_property_set> XML section of key/value pairs for cluster-wide configuration settings."""
def __init__(self, factory, _id="cib-bootstrap-options"):
- """ Create a new Option instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
"""
+ Create a new Option instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ """
XmlBase.__init__(self, factory, "cluster_property_set", _id)
def __setitem__(self, key, value):
- """ Add a child nvpair element containing the given key/value pair """
-
+ """Add a child nvpair element containing the given key/value pair."""
self.add_child(XmlBase(self._factory, "nvpair", "cts-%s" % key, name=key, value=value))
def commit(self):
- """ Modify the CIB on the cluster to include this XML section """
-
+ """Modify the CIB on the cluster to include this XML section."""
self._run("modify", self.show(), "crm_config", "--allow-create")
class OpDefaults(XmlBase):
- """ A class that creates a <cts-op_defaults-meta> XML section of key/value
- pairs for operation default settings
- """
+ """Create a <cts-op_defaults-meta> XML section of key/value pairs for operation default settings."""
def __init__(self, factory):
- """ Create a new OpDefaults instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
"""
+ Create a new OpDefaults instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ """
XmlBase.__init__(self, factory, "op_defaults", None)
self.meta = XmlBase(self._factory, "meta_attributes", "cts-op_defaults-meta")
self.add_child(self.meta)
def __setitem__(self, key, value):
- """ Add a child nvpair meta_attribute element containing the given
- key/value pair
- """
-
+ """Add a child nvpair meta_attribute element containing the given key/value pair."""
self.meta.add_child(XmlBase(self._factory, "nvpair", "cts-op_defaults-%s" % key, name=key, value=value))
def commit(self):
- """ Modify the CIB on the cluster to include this XML section """
-
+ """Modify the CIB on the cluster to include this XML section."""
self._run("modify", self.show(), "configuration", "--allow-create")
class Alerts(XmlBase):
- """ A class that creates an <alerts> XML section """
+ """Create an <alerts> XML section."""
def __init__(self, factory):
- """ Create a new Alerts instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
"""
+ Create a new Alerts instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ """
XmlBase.__init__(self, factory, "alerts", None)
self._alert_count = 0
def add_alert(self, path, recipient):
- """ Create a new alert as a child of this XML section
-
- Arguments:
-
- path -- The path to a script to be called when a cluster
- event occurs
- recipient -- An environment variable to be passed to the script
"""
+ Create a new alert as a child of this XML section.
+ Arguments:
+ path -- The path to a script to be called when a cluster
+ event occurs
+ recipient -- An environment variable to be passed to the script
+ """
self._alert_count += 1
alert = XmlBase(self._factory, "alert", "alert-%d" % self._alert_count,
path=path)
@@ -377,54 +341,47 @@ class Alerts(XmlBase):
self.add_child(alert)
def commit(self):
- """ Modify the CIB on the cluster to include this XML section """
-
+ """Modify the CIB on the cluster to include this XML section."""
self._run("modify", self.show(), "configuration", "--allow-create")
class Expression(XmlBase):
- """ A class that creates an <expression> XML element as part of some
- constraint rule
- """
+ """Create an <expression> XML element as part of some constraint rule."""
def __init__(self, factory, _id, attr, op, value=None):
- """ Create a new Expression instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
- attr -- The attribute to be tested
- op -- The comparison to perform ("lt", "eq", "defined", etc.)
- value -- Value for comparison (can be None for "defined" and
- "not_defined" operations)
"""
-
+ Create a new Expression instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ attr -- The attribute to be tested
+ op -- The comparison to perform ("lt", "eq", "defined", etc.)
+ value -- Value for comparison (can be None for "defined" and
+ "not_defined" operations)
+ """
XmlBase.__init__(self, factory, "expression", _id, attribute=attr, operation=op)
if value:
self["value"] = value
class Rule(XmlBase):
- """ A class that creates a <rule> XML section consisting of one or more
- expressions, as part of some constraint
- """
+ """Create a <rule> XML section consisting of one or more expressions, as part of some constraint."""
def __init__(self, factory, _id, score, op="and", expr=None):
- """ Create a new Rule instance
-
- Arguments:
-
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
- score -- If this rule is used in a location constraint and
- evaluates to true, apply this score to the constraint
- op -- If this rule contains more than one expression, use this
- boolean op when evaluating
- expr -- An Expression instance that can be added to this Rule
- when it is created
"""
-
+ Create a new Rule instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ score -- If this rule is used in a location constraint and
+ evaluates to true, apply this score to the constraint
+ op -- If this rule contains more than one expression, use this
+ boolean op when evaluating
+ expr -- An Expression instance that can be added to this Rule
+ when it is created
+ """
XmlBase.__init__(self, factory, "rule", _id)
self["boolean-op"] = op
@@ -435,24 +392,25 @@ class Rule(XmlBase):
class Resource(XmlBase):
- """ A base class that creates all kinds of <resource> XML sections fully
- describing a single cluster resource. This defaults to primitive
- resources, but subclasses can create other types.
"""
+ A base class that creates all kinds of <resource> XML sections.
- def __init__(self, factory, _id, rtype, standard, provider=None):
- """ Create a new Resource instance
-
- Arguments:
+ These sections fully describe a single cluster resource. This defaults to
+ primitive resources, but subclasses can create other types.
+ """
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
- rtype -- The name of the resource agent
- standard -- The standard the resource agent follows ("ocf",
- "systemd", etc.)
- provider -- The vendor providing the resource agent
+ def __init__(self, factory, _id, rtype, standard, provider=None):
+ """
+ Create a new Resource instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ rtype -- The name of the resource agent
+ standard -- The standard the resource agent follows ("ocf",
+ "systemd", etc.)
+ provider -- The vendor providing the resource agent
"""
-
XmlBase.__init__(self, factory, "native", _id)
self._provider = provider
@@ -473,52 +431,41 @@ class Resource(XmlBase):
self._provider = None
def __setitem__(self, key, value):
- """ Add a child nvpair element containing the given key/value pair as
- an instance attribute
- """
-
+ """Add a child nvpair element containing the given key/value pair as an instance attribute."""
self._add_param(key, value)
def add_op(self, _id, interval, **kwargs):
- """ Add an operation child XML element to this resource
-
- Arguments:
-
- _id -- A unique name for the element. Also, the action to
- perform ("monitor", "start", "stop", etc.)
- interval -- How frequently (in seconds) to perform the operation
- kwargs -- Any additional key/value pairs that should be added to
- this element as attributes
"""
-
+ Add an operation child XML element to this resource.
+
+ Arguments:
+ _id -- A unique name for the element. Also, the action to
+ perform ("monitor", "start", "stop", etc.)
+ interval -- How frequently (in seconds) to perform the operation
+ kwargs -- Any additional key/value pairs that should be added to
+ this element as attributes
+ """
self._op.append(XmlBase(self._factory, "op", "%s-%s" % (_id, interval),
name=_id, interval=interval, **kwargs))
def _add_param(self, name, value):
- """ Add a child nvpair element containing the given key/value pair as
- an instance attribute
- """
-
+ """Add a child nvpair element containing the given key/value pair as an instance attribute."""
self._param[name] = value
def add_meta(self, name, value):
- """ Add a child nvpair element containing the given key/value pair as
- a meta attribute
- """
-
+ """Add a child nvpair element containing the given key/value pair as a meta attribute."""
self._meta[name] = value
def prefer(self, node, score="INFINITY", rule=None):
- """ Add a location constraint where this resource prefers some node
-
- Arguments:
-
- node -- The name of the node to prefer
- score -- Apply this score to the location constraint
- rule -- A Rule instance to use in creating this constraint, instead
- of creating a new rule
"""
+ Add a location constraint where this resource prefers some node.
+ Arguments:
+ node -- The name of the node to prefer
+ score -- Apply this score to the location constraint
+ rule -- A Rule instance to use in creating this constraint, instead
+ of creating a new rule
+ """
if not rule:
rule = Rule(self._factory, "prefer-%s-r" % node, score,
expr=Expression(self._factory, "prefer-%s-e" % node, "#uname", "eq", node))
@@ -526,23 +473,22 @@ class Resource(XmlBase):
self._scores[node] = rule
def after(self, resource, kind="Mandatory", first="start", then="start", **kwargs):
- """ Create an ordering constraint between this resource and some other
-
- Arguments:
-
- resource -- The name of the dependent resource
- kind -- How to enforce the constraint ("mandatory", "optional",
- "serialize")
- first -- The action that this resource must complete before the
- then-action can be initiated for the dependent resource
- ("start", "stop", "promote", "demote")
- then -- The action that the dependent resource can execute only
- after the first-action has completed (same values as
- first)
- kwargs -- Any additional key/value pairs that should be added to
- this element as attributes
"""
-
+ Create an ordering constraint between this resource and some other.
+
+ Arguments:
+ resource -- The name of the dependent resource
+ kind -- How to enforce the constraint ("mandatory", "optional",
+ "serialize")
+ first -- The action that this resource must complete before the
+ then-action can be initiated for the dependent resource
+ ("start", "stop", "promote", "demote")
+ then -- The action that the dependent resource can execute only
+ after the first-action has completed (same values as
+ first)
+ kwargs -- Any additional key/value pairs that should be added to
+ this element as attributes
+ """
kargs = kwargs.copy()
kargs["kind"] = kind
@@ -556,21 +502,20 @@ class Resource(XmlBase):
self._needs[resource] = kargs
def colocate(self, resource, score="INFINITY", role=None, withrole=None, **kwargs):
- """ Create a colocation constraint between this resource and some other
-
- Arguments:
-
- resource -- The name of the resource that should be located relative
- this one
- score -- Apply this score to the colocation constraint
- role -- Apply this colocation constraint only to promotable clones
- in this role ("started", "promoted", "unpromoted")
- withrole -- Apply this colocation constraint only to with-rsc promotable
- clones in this role
- kwargs -- Any additional key/value pairs that should be added to
- this element as attributes
"""
-
+ Create a colocation constraint between this resource and some other.
+
+ Arguments:
+ resource -- The name of the resource that should be located relative
+ this one
+ score -- Apply this score to the colocation constraint
+ role -- Apply this colocation constraint only to promotable clones
+ in this role ("started", "promoted", "unpromoted")
+ withrole -- Apply this colocation constraint only to with-rsc promotable
+ clones in this role
+ kwargs -- Any additional key/value pairs that should be added to
+ this element as attributes
+ """
kargs = kwargs.copy()
kargs["score"] = score
@@ -583,10 +528,7 @@ class Resource(XmlBase):
self._coloc[resource] = kargs
def _constraints(self):
- """ Generate a <constraints> XML section containing all previously added
- ordering and colocation constraints
- """
-
+ """Generate a <constraints> XML section containing all previously added ordering and colocation constraints."""
text = "<constraints>"
for (k, v) in self._scores.items():
@@ -605,10 +547,7 @@ class Resource(XmlBase):
return text
def show(self):
- """ Return a string representation of this XML section, including all
- of its children
- """
-
+ """Recursively return a string representation of this XML section."""
text = '''<primitive id="%s" class="%s" type="%s"''' % (self.name, self._standard, self._rtype)
if self._provider:
@@ -649,38 +588,36 @@ class Resource(XmlBase):
return text
def commit(self):
- """ Modify the CIB on the cluster to include this XML section """
-
+ """Modify the CIB on the cluster to include this XML section."""
self._run("create", self.show(), "resources")
self._run("modify", self._constraints(), "constraints")
class Group(Resource):
- """ A specialized Resource subclass that creates a <group> XML section
- describing a single group resource consisting of multiple child
- primitive resources
"""
+ A specialized Resource subclass that creates a <group> XML section.
- def __init__(self, factory, _id):
- """ Create a new Group instance
-
- Arguments:
+ This section describes a single group resource consisting of multiple child
+ primitive resources.
+ """
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
+ def __init__(self, factory, _id):
"""
+ Create a new Group instance.
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ """
Resource.__init__(self, factory, _id, None, None)
self.tag = "group"
def __setitem__(self, key, value):
+ """Add a child nvpair element containing the given key/value pair as an instance attribute."""
self.add_meta(key, value)
def show(self):
- """ Return a string representation of this XML section, including all
- of its children
- """
-
+ """Recursively return a string representation of this XML section."""
text = '''<%s id="%s">''' % (self.tag, self.name)
if len(self._meta) > 0:
@@ -700,23 +637,24 @@ class Group(Resource):
class Clone(Group):
- """ A specialized Group subclass that creates a <clone> XML section
- describing a clone resource containing multiple instances of a
- single primitive resource
"""
+ A specialized Group subclass that creates a <clone> XML section.
- def __init__(self, factory, _id, child=None):
- """ Create a new Clone instance
-
- Arguments:
+ This section describes a clone resource containing multiple instances of a
+ single primitive resource.
+ """
- factory -- A ConfigFactory instance
- _id -- A unique name for the element
- child -- A Resource instance that can be added to this Clone
- when it is created. Alternately, use add_child later.
- Note that a Clone may only have one child.
+ def __init__(self, factory, _id, child=None):
+ """
+ Create a new Clone instance.
+
+ Arguments:
+ factory -- A ConfigFactory instance
+ _id -- A unique name for the element
+ child -- A Resource instance that can be added to this Clone
+ when it is created. Alternately, use add_child later.
+ Note that a Clone may only have one child.
"""
-
Group.__init__(self, factory, _id)
self.tag = "clone"
@@ -724,10 +662,11 @@ class Clone(Group):
self.add_child(child)
def add_child(self, child):
- """ Add the given resource as a child of this Clone. Note that a
- Clone resource only supports one child at a time.
"""
+ Add the given resource as a child of this Clone.
+ Note that a Clone resource only supports one child at a time.
+ """
if not self._children:
self._children.append(child)
else:
diff --git a/python/pacemaker/_cts/clustermanager.py b/python/pacemaker/_cts/clustermanager.py
index 652108f..0e9b1cf 100644
--- a/python/pacemaker/_cts/clustermanager.py
+++ b/python/pacemaker/_cts/clustermanager.py
@@ -1,7 +1,7 @@
-""" ClusterManager class for Pacemaker's Cluster Test Suite (CTS) """
+"""ClusterManager class for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["ClusterManager"]
-__copyright__ = """Copyright 2000-2023 the Pacemaker project contributors.
+__copyright__ = """Copyright 2000-2024 the Pacemaker project contributors.
Certain portions by Huang Zhen <zhenhltc@cn.ibm.com> are copyright 2004
International Business Machines. The version control history for this file
may have further details."""
@@ -34,31 +34,33 @@ from pacemaker._cts.watcher import LogWatcher
# ClusterManager has a lot of methods.
# pylint: disable=too-many-public-methods
+
class ClusterManager(UserDict):
- """ An abstract base class for managing the cluster. This class implements
- high-level operations on the cluster and/or its cluster managers.
- Actual cluster-specific management classes should be subclassed from this
- one.
+ """
+ An abstract base class for managing the cluster.
- Among other things, this class tracks the state every node is expected to
- be in.
+ This class implements high-level operations on the cluster and/or its cluster
+ managers. Actual cluster-specific management classes should be subclassed
+ from this one.
+
+ Among other things, this class tracks the state every node is expected to be in.
"""
def _final_conditions(self):
- """ Check all keys to make sure they have a non-None value """
-
+ """Check all keys to make sure they have a non-None value."""
for (key, val) in self._data.items():
if val is None:
raise ValueError("Improper derivation: self[%s] must be overridden by subclass." % key)
def __init__(self):
- """ Create a new ClusterManager instance. This class can be treated
- kind of like a dictionary due to the process of certain dict
- functions like __getitem__ and __setitem__. This is because it
- contains a lot of name/value pairs. However, it is not actually
- a dictionary so do not rely on standard dictionary behavior.
"""
+ Create a new ClusterManager instance.
+ This class can be treated kind of like a dictionary due to the process
+ of certain dict functions like __getitem__ and __setitem__. This is
+ because it contains a lot of name/value pairs. However, it is not
+ actually a dictionary so do not rely on standard dictionary behavior.
+ """
# Eventually, ClusterManager should not be a UserDict subclass. Until
# that point...
# pylint: disable=super-init-not-called
@@ -85,6 +87,15 @@ class ClusterManager(UserDict):
self._cib_sync = {}
def __getitem__(self, key):
+ """
+ Return the given key, checking for it in several places.
+
+ If key is "Name", return the name of the cluster manager. If the key
+ was previously added to the dictionary via __setitem__, return that.
+ Otherwise, return the template pattern for the key.
+
+ This method should not be used and may be removed in the future.
+ """
if key == "Name":
return self.name
@@ -95,41 +106,38 @@ class ClusterManager(UserDict):
return self.templates.get_patterns(key)
def __setitem__(self, key, value):
+ """
+ Set the given key to the given value, overriding any previous value.
+
+ This method should not be used and may be removed in the future.
+ """
print("FIXME: Setting %s=%s on %r" % (key, value, self))
self._data[key] = value
def clear_instance_errors_to_ignore(self):
- """ Reset instance-specific errors to ignore on each iteration """
-
+ """Reset instance-specific errors to ignore on each iteration."""
self.__instance_errors_to_ignore = []
@property
def instance_errors_to_ignore(self):
- """ Return a list of known errors that should be ignored for a specific
- test instance
- """
-
+ """Return a list of known errors that should be ignored for a specific test instance."""
return self.__instance_errors_to_ignore
@property
def errors_to_ignore(self):
- """ Return a list of known error messages that should be ignored """
-
+ """Return a list of known error messages that should be ignored."""
return self.templates.get_patterns("BadNewsIgnore")
def log(self, args):
- """ Log a message """
-
+ """Log a message."""
self._logger.log(args)
def debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
self._logger.debug(args)
def upcount(self):
- """ How many nodes are up? """
-
+ """Return how many nodes are up."""
count = 0
for node in self.env["nodes"]:
@@ -139,16 +147,16 @@ class ClusterManager(UserDict):
return count
def install_support(self, command="install"):
- """ Install or uninstall the CTS support files - various init scripts and data,
- daemons, fencing agents, etc.
"""
+ Install or uninstall the CTS support files.
+ This includes various init scripts and data, daemons, fencing agents, etc.
+ """
for node in self.env["nodes"]:
self.rsh(node, "%s/cts-support %s" % (BuildOptions.DAEMON_DIR, command))
def prepare_fencing_watcher(self):
- """ Return a LogWatcher object that watches for fencing log messages """
-
+ """Return a LogWatcher object that watches for fencing log messages."""
# If we don't have quorum now but get it as a result of starting this node,
# then a bunch of nodes might get fenced
if self.has_quorum(None):
@@ -175,13 +183,12 @@ class ClusterManager(UserDict):
])
stonith = LogWatcher(self.env["LogFileName"], stonith_pats, self.env["nodes"],
- self.env["LogWatcher"], "StartupFencing", 0)
+ self.env["log_kind"], "StartupFencing", 0)
stonith.set_watch()
return stonith
def fencing_cleanup(self, node, stonith):
- """ Wait for a previously fenced node to return to the cluster """
-
+ """Wait for a previously fenced node to return to the cluster."""
peer_list = []
peer_state = {}
@@ -225,7 +232,7 @@ class ClusterManager(UserDict):
if not peer:
self._logger.log("ERROR: Unknown stonith match: %r" % shot)
- elif not peer in peer_list:
+ elif peer not in peer_list:
self.debug("Found peer: %s" % peer)
peer_list.append(peer)
@@ -263,14 +270,13 @@ class ClusterManager(UserDict):
return peer_list
def start_cm(self, node, verbose=False):
- """ Start up the cluster manager on a given node """
-
+ """Start up the cluster manager on a given node."""
if verbose:
self._logger.log("Starting %s on node %s" % (self.templates["Name"], node))
else:
self.debug("Starting %s on node %s" % (self.templates["Name"], node))
- if not node in self.expected_status:
+ if node not in self.expected_status:
self.expected_status[node] = "down"
if self.expected_status[node] != "down":
@@ -286,7 +292,8 @@ class ClusterManager(UserDict):
else:
patterns.append(self.templates["Pat:NonDC_started"] % node)
- watch = LogWatcher(self.env["LogFileName"], patterns, self.env["nodes"], self.env["LogWatcher"],
+ watch = LogWatcher(self.env["LogFileName"], patterns,
+ self.env["nodes"], self.env["log_kind"],
"StartaCM", self.env["StartTime"] + 10)
self.install_config(node)
@@ -325,8 +332,7 @@ class ClusterManager(UserDict):
return False
def start_cm_async(self, node, verbose=False):
- """ Start up the cluster manager on a given node without blocking """
-
+ """Start up the cluster manager on a given node without blocking."""
if verbose:
self._logger.log("Starting %s on node %s" % (self["Name"], node))
else:
@@ -337,8 +343,7 @@ class ClusterManager(UserDict):
self.expected_status[node] = "up"
def stop_cm(self, node, verbose=False, force=False):
- """ Stop the cluster manager on a given node """
-
+ """Stop the cluster manager on a given node."""
if verbose:
self._logger.log("Stopping %s on node %s" % (self["Name"], node))
else:
@@ -358,18 +363,14 @@ class ClusterManager(UserDict):
return False
def stop_cm_async(self, node):
- """ Stop the cluster manager on a given node without blocking """
-
+ """Stop the cluster manager on a given node without blocking."""
self.debug("Stopping %s on node %s" % (self["Name"], node))
self.rsh(node, self.templates["StopCmd"], synchronous=False)
self.expected_status[node] = "down"
def startall(self, nodelist=None, verbose=False, quick=False):
- """ Start the cluster manager on every node in the cluster, or on every
- node in nodelist if not None
- """
-
+ """Start the cluster manager on every node in the cluster, or on every node in nodelist."""
if not nodelist:
nodelist = self.env["nodes"]
@@ -395,7 +396,8 @@ class ClusterManager(UserDict):
# Start all the nodes - at about the same time...
watch = LogWatcher(self.env["LogFileName"], watchpats, self.env["nodes"],
- self.env["LogWatcher"], "fast-start", self.env["DeadTime"] + 10)
+ self.env["log_kind"], "fast-start",
+ self.env["DeadTime"] + 10)
watch.set_watch()
if not self.start_cm(nodelist[0], verbose=verbose):
@@ -416,10 +418,7 @@ class ClusterManager(UserDict):
return True
def stopall(self, nodelist=None, verbose=False, force=False):
- """ Stop the cluster manager on every node in the cluster, or on every
- node in nodelist if not None
- """
-
+ """Stop the cluster manager on every node in the cluster, or on every node in nodelist."""
ret = True
if not nodelist:
@@ -433,10 +432,7 @@ class ClusterManager(UserDict):
return ret
def statall(self, nodelist=None):
- """ Return the status of the cluster manager on every node in the cluster,
- or on every node in nodelist if not None
- """
-
+ """Return the status of the cluster manager on every node in the cluster, or on every node in nodelist."""
result = {}
if not nodelist:
@@ -451,10 +447,7 @@ class ClusterManager(UserDict):
return result
def isolate_node(self, target, nodes=None):
- """ Break communication between the target node and all other nodes in the
- cluster, or nodes if not None
- """
-
+ """Break communication between the target node and all other nodes in the cluster, or nodes."""
if not nodes:
nodes = self.env["nodes"]
@@ -472,10 +465,7 @@ class ClusterManager(UserDict):
return True
def unisolate_node(self, target, nodes=None):
- """ Re-establish communication between the target node and all other nodes
- in the cluster, or nodes if not None
- """
-
+ """Re-establish communication between the target node and all other nodes in the cluster, or nodes."""
if not nodes:
nodes = self.env["nodes"]
@@ -490,8 +480,7 @@ class ClusterManager(UserDict):
self.debug("Communication restored between %s and %s" % (target, node))
def oprofile_start(self, node=None):
- """ Start profiling on the given node, or all nodes in the cluster """
-
+ """Start profiling on the given node, or all nodes in the cluster."""
if not node:
for n in self.env["oprofile"]:
self.oprofile_start(n)
@@ -504,10 +493,7 @@ class ClusterManager(UserDict):
self.rsh(node, "opcontrol --reset")
def oprofile_save(self, test, node=None):
- """ Save profiling data and restart profiling on the given node, or all
- nodes in the cluster if None
- """
-
+ """Save profiling data and restart profiling on the given node, or all nodes in the cluster."""
if not node:
for n in self.env["oprofile"]:
self.oprofile_save(test, n)
@@ -520,10 +506,11 @@ class ClusterManager(UserDict):
self.oprofile_start(node)
def oprofile_stop(self, node=None):
- """ Start profiling on the given node, or all nodes in the cluster. This
- does not save profiling data, so call oprofile_save first if needed.
"""
+ Start profiling on the given node, or all nodes in the cluster.
+ This does not save profiling data, so call oprofile_save first if needed.
+ """
if not node:
for n in self.env["oprofile"]:
self.oprofile_stop(n)
@@ -534,8 +521,7 @@ class ClusterManager(UserDict):
self.rsh(node, "opcontrol --shutdown 2>&1 > /dev/null")
def install_config(self, node):
- """ Remove and re-install the CIB on the first node in the cluster """
-
+ """Remove and re-install the CIB on the first node in the cluster."""
if not self.ns.wait_for_node(node):
self.log("Node %s is not up." % node)
return
@@ -566,10 +552,12 @@ class ClusterManager(UserDict):
self.rsh(node, "chown %s %s/cib.xml" % (BuildOptions.DAEMON_USER, BuildOptions.CIB_DIR))
def prepare(self):
- """ Finish initialization by clearing out the expected status and recording
- the current status of every node in the cluster
"""
+ Finish initialization.
+ Clear out the expected status and record the current status of every
+ node in the cluster.
+ """
self.partitions_expected = 1
for node in self.env["nodes"]:
self.expected_status[node] = ""
@@ -580,11 +568,12 @@ class ClusterManager(UserDict):
self.stat_cm(node)
def test_node_cm(self, node):
- """ Check the status of a given node. Returns 0 if the node is
- down, 1 if the node is up but unstable, and 2 if the node is
- up and stable
"""
+ Check the status of a given node.
+ Returns 0 if the node is down, 1 if the node is up but unstable, and 2
+ if the node is up and stable.
+ """
watchpats = [
"Current ping state: (S_IDLE|S_NOT_DC)",
self.templates["Pat:NonDC_started"] % node,
@@ -592,7 +581,7 @@ class ClusterManager(UserDict):
]
idle_watch = LogWatcher(self.env["LogFileName"], watchpats, [node],
- self.env["LogWatcher"], "ClusterIdle")
+ self.env["log_kind"], "ClusterIdle")
idle_watch.set_watch()
(_, out) = self.rsh(node, self.templates["StatusCmd"] % node, verbose=1)
@@ -637,14 +626,12 @@ class ClusterManager(UserDict):
return 2
def stat_cm(self, node):
- """ Report the status of the cluster manager on a given node """
-
+ """Report the status of the cluster manager on a given node."""
return self.test_node_cm(node) > 0
# Being up and being stable is not the same question...
def node_stable(self, node):
- """ Return whether or not the given node is stable """
-
+ """Return whether or not the given node is stable."""
if self.test_node_cm(node) == 2:
return True
@@ -652,8 +639,7 @@ class ClusterManager(UserDict):
return False
def partition_stable(self, nodes, timeout=None):
- """ Return whether or not all nodes in the given partition are stable """
-
+ """Return whether or not all nodes in the given partition are stable."""
watchpats = [
"Current ping state: S_IDLE",
self.templates["Pat:DC_IDLE"],
@@ -669,7 +655,7 @@ class ClusterManager(UserDict):
return True
idle_watch = LogWatcher(self.env["LogFileName"], watchpats, nodes.split(),
- self.env["LogWatcher"], "ClusterStable", timeout)
+ self.env["log_kind"], "ClusterStable", timeout)
idle_watch.set_watch()
for node in nodes.split():
@@ -691,8 +677,7 @@ class ClusterManager(UserDict):
return False
def cluster_stable(self, timeout=None, double_check=False):
- """ Return whether or not all nodes in the cluster are stable """
-
+ """Return whether or not all nodes in the cluster are stable."""
partitions = self.find_partitions()
for partition in partitions:
@@ -713,10 +698,11 @@ class ClusterManager(UserDict):
return True
def is_node_dc(self, node, status_line=None):
- """ Return whether or not the given node is the cluster DC by checking
- the given status_line, or by querying the cluster if None
"""
+ Return whether or not the given node is the cluster DC.
+ Check the given status_line, or query the cluster if None.
+ """
if not status_line:
(_, out) = self.rsh(node, self.templates["StatusCmd"] % node, verbose=1)
@@ -744,8 +730,7 @@ class ClusterManager(UserDict):
return False
def active_resources(self, node):
- """ Return a list of primitive resources active on the given node """
-
+ """Return a list of primitive resources active on the given node."""
(_, output) = self.rsh(node, "crm_resource -c", verbose=1)
resources = []
for line in output:
@@ -759,8 +744,7 @@ class ClusterManager(UserDict):
return resources
def resource_location(self, rid):
- """ Return a list of nodes on which the given resource is running """
-
+ """Return a list of nodes on which the given resource is running."""
resource_nodes = []
for node in self.env["nodes"]:
if self.expected_status[node] != "up":
@@ -780,10 +764,12 @@ class ClusterManager(UserDict):
return resource_nodes
def find_partitions(self):
- """ Return a list of all partitions in the cluster. Each element of the
- list is itself a list of all active nodes in that partition.
"""
+ Return a list of all partitions in the cluster.
+ Each element of the list is itself a list of all active nodes in that
+ partition.
+ """
ccm_partitions = []
for node in self.env["nodes"]:
@@ -822,8 +808,7 @@ class ClusterManager(UserDict):
return ccm_partitions
def has_quorum(self, node_list):
- """ Return whether or not the cluster has quorum """
-
+ """Return whether or not the cluster has quorum."""
# If we are auditing a partition, then one side will
# have quorum and the other not.
# So the caller needs to tell us which we are checking
@@ -850,15 +835,15 @@ class ClusterManager(UserDict):
@property
def components(self):
- """ A list of all patterns that should be ignored for the cluster's
- components. This must be provided by all subclasses.
"""
+ Return a list of all patterns that should be ignored for the cluster's components.
+ This must be provided by all subclasses.
+ """
raise NotImplementedError
def in_standby_mode(self, node):
- """ Return whether or not the node is in Standby """
-
+ """Return whether or not the node is in Standby."""
(_, out) = self.rsh(node, self.templates["StandbyQueryCmd"] % node, verbose=1)
if not out:
@@ -869,10 +854,11 @@ class ClusterManager(UserDict):
return out == "on"
def set_standby_mode(self, node, status):
- """ Set node to Standby if status is True, or Active if status is False.
- Return whether the node is now in the requested status.
"""
+ Set node to Standby if status is True, or Active if status is False.
+ Return whether the node is now in the requested status.
+ """
current_status = self.in_standby_mode(node)
if current_status == status:
@@ -887,8 +873,7 @@ class ClusterManager(UserDict):
return rc == 0
def add_dummy_rsc(self, node, rid):
- """ Add a dummy resource with the given ID to the given node """
-
+ """Add a dummy resource with the given ID to the given node."""
rsc_xml = """ '<resources>
<primitive class=\"ocf\" id=\"%s\" provider=\"pacemaker\" type=\"Dummy\">
<operations>
@@ -905,10 +890,7 @@ class ClusterManager(UserDict):
self.rsh(node, self.templates['CibAddXml'] % constraint_xml)
def remove_dummy_rsc(self, node, rid):
- """ Remove the previously added dummy resource given by rid on the
- given node
- """
-
+ """Remove the previously added dummy resource given by rid on the given node."""
constraint = "\"//rsc_location[@rsc='%s']\"" % rid
rsc = "\"//primitive[@id='%s']\"" % rid
diff --git a/python/pacemaker/_cts/cmcorosync.py b/python/pacemaker/_cts/cmcorosync.py
index cac059b..f64b811 100644
--- a/python/pacemaker/_cts/cmcorosync.py
+++ b/python/pacemaker/_cts/cmcorosync.py
@@ -1,7 +1,7 @@
-""" Corosync-specific class for Pacemaker's Cluster Test Suite (CTS) """
+"""Corosync-specific class for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["Corosync2"]
-__copyright__ = "Copyright 2007-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2007-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.CTS import Process
@@ -14,14 +14,12 @@ from pacemaker._cts.patterns import PatternSelector
# self._rsh in environment.py.
# pylint: disable=unsubscriptable-object
+
class Corosync2(ClusterManager):
- """ A subclass of ClusterManager specialized to handle corosync2 and later
- based clusters
- """
+ """A subclass of ClusterManager specialized to handle corosync2 and later based clusters."""
def __init__(self):
- """ Create a new Corosync2 instance """
-
+ """Create a new Corosync2 instance."""
ClusterManager.__init__(self)
self._fullcomplist = {}
@@ -29,10 +27,7 @@ class Corosync2(ClusterManager):
@property
def components(self):
- """ A list of all patterns that should be ignored for the cluster's
- components.
- """
-
+ """Return a list of patterns that should be ignored for the cluster's components."""
complist = []
if not self._fullcomplist:
diff --git a/python/pacemaker/_cts/corosync.py b/python/pacemaker/_cts/corosync.py
index aabaecd..af3b1de 100644
--- a/python/pacemaker/_cts/corosync.py
+++ b/python/pacemaker/_cts/corosync.py
@@ -1,7 +1,7 @@
-""" A module providing functions for manipulating corosync """
+"""A module providing functions for manipulating corosync."""
__all__ = ["Corosync", "localname"]
-__copyright__ = "Copyright 2009-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2009-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+)"
import os
@@ -40,14 +40,12 @@ logging {
def corosync_cfg_exists():
- """ Does the corosync config file exist? """
-
+ """Return whether the corosync config file exists."""
return os.path.exists(BuildOptions.COROSYNC_CONFIG_FILE)
def corosync_log_file(cfgfile):
- """ Where does corosync log to? """
-
+ """Return the path to the corosync log file, or None."""
with open(cfgfile, "r", encoding="utf-8") as f:
for line in f.readlines():
# "to_logfile:" could also be in the config file, so check for a
@@ -59,8 +57,7 @@ def corosync_log_file(cfgfile):
def generate_corosync_cfg(logdir, cluster_name, node_name):
- """ Generate the corosync config file, if it does not already exist """
-
+ """Generate the corosync config file, if it does not already exist."""
if corosync_cfg_exists():
return False
@@ -73,8 +70,7 @@ def generate_corosync_cfg(logdir, cluster_name, node_name):
def localname():
- """ Return the uname of the local host """
-
+ """Return the uname of the local host."""
our_uname = stdout_from_command(["uname", "-n"])
if our_uname:
our_uname = our_uname[0]
@@ -85,18 +81,17 @@ def localname():
class Corosync:
- """ A class for managing corosync processes and config files """
+ """A class for managing corosync processes and config files."""
def __init__(self, verbose, logdir, cluster_name):
- """ Create a new Corosync instance.
-
- Arguments:
-
- verbose -- Whether to print the corosync log file
- logdir -- The base directory under which to store log files
- cluster_name -- The name of the cluster
"""
+ Create a new Corosync instance.
+ Arguments:
+ verbose -- Whether to print the corosync log file
+ logdir -- The base directory under which to store log files
+ cluster_name -- The name of the cluster
+ """
self.verbose = verbose
self.logdir = logdir
self.cluster_name = cluster_name
@@ -104,8 +99,7 @@ class Corosync:
self._generated_cfg_file = False
def _ready(self, logfile, timeout=10):
- """ Is corosync ready to go? """
-
+ """Return whether corosync is ready."""
i = 0
while i < timeout:
@@ -123,16 +117,15 @@ class Corosync:
raise TimeoutError
def start(self, kill_first=False, timeout=10):
- """ Start the corosync process
-
- Arguments:
-
- kill_first -- Whether to kill any pre-existing corosync processes before
- starting a new one
- timeout -- If corosync does not start within this many seconds, raise
- TimeoutError
"""
+ Start the corosync process.
+ Arguments:
+ kill_first -- Whether to kill any pre-existing corosync processes before
+ starting a new one
+ timeout -- If corosync does not start within this many seconds, raise
+ TimeoutError
+ """
if kill_first:
killall(["corosync"])
@@ -150,8 +143,7 @@ class Corosync:
self._ready(logfile, timeout=timeout)
def stop(self):
- """ Stop the corosync process """
-
+ """Stop the corosync process."""
killall(["corosync"])
# If we did not write out the corosync config file, don't do anything else.
diff --git a/python/pacemaker/_cts/environment.py b/python/pacemaker/_cts/environment.py
index 732ab24..2407414 100644
--- a/python/pacemaker/_cts/environment.py
+++ b/python/pacemaker/_cts/environment.py
@@ -1,7 +1,7 @@
-""" Test environment classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Test environment classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["EnvFactory"]
-__copyright__ = "Copyright 2014-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2014-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import argparse
@@ -16,9 +16,12 @@ from pacemaker._cts.logging import LogFactory
from pacemaker._cts.remote import RemoteFactory
from pacemaker._cts.watcher import LogKind
+
class Environment:
- """ A class for managing the CTS environment, consisting largely of processing
- and storing command line parameters
+ """
+ A class for managing the CTS environment.
+
+ This consists largely of processing and storing command line parameters.
"""
# pylint doesn't understand that self._rsh is callable (it stores the
@@ -30,17 +33,18 @@ class Environment:
# pylint: disable=not-callable
def __init__(self, args):
- """ Create a new Environment instance. This class can be treated kind
- of like a dictionary due to the presence of typical dict functions
- like __contains__, __getitem__, and __setitem__. However, it is not a
- dictionary so do not rely on standard dictionary behavior.
+ """
+ Create a new Environment instance.
- Arguments:
+ This class can be treated kind of like a dictionary due to the presence
+ of typical dict functions like __contains__, __getitem__, and __setitem__.
+ However, it is not a dictionary so do not rely on standard dictionary
+ behavior.
- args -- A list of command line parameters, minus the program name.
- If None, sys.argv will be used.
+ Arguments:
+ args -- A list of command line parameters, minus the program name.
+ If None, sys.argv will be used.
"""
-
self.data = {}
self._nodes = []
@@ -56,7 +60,7 @@ class Environment:
self["ClobberCIB"] = False
self["CIBfilename"] = None
self["CIBResource"] = False
- self["LogWatcher"] = LogKind.ANY
+ self["log_kind"] = None
self["node-limit"] = 0
self["scenario"] = "random"
@@ -74,10 +78,13 @@ class Environment:
self._discover()
def _seed_random(self, seed=None):
- """ Initialize the random number generator with the given seed, or use
- the current time if None
"""
+ Initialize the random number generator.
+ Arguments:
+ seed -- Use this to see the random number generator, or use the
+ current time if None.
+ """
if not seed:
seed = int(time.time())
@@ -85,8 +92,7 @@ class Environment:
self.random_gen.seed(str(seed))
def dump(self):
- """ Print the current environment """
-
+ """Print the current environment."""
keys = []
for key in list(self.data.keys()):
keys.append(key)
@@ -97,21 +103,18 @@ class Environment:
self._logger.debug("{key:35}: {val}".format(key=s, val=str(self[key])))
def keys(self):
- """ Return a list of all environment keys stored in this instance """
-
+ """Return a list of all environment keys stored in this instance."""
return list(self.data.keys())
def __contains__(self, key):
- """ Does the given environment key exist? """
-
+ """Return True if the given key exists in the environment."""
if key == "nodes":
return True
return key in self.data
def __getitem__(self, key):
- """ Return the given environment key, or None if it does not exist """
-
+ """Return the given environment key, or None if it does not exist."""
if str(key) == "0":
raise ValueError("Bad call to 'foo in X', should reference 'foo in X.keys()' instead")
@@ -124,10 +127,7 @@ class Environment:
return self.data.get(key)
def __setitem__(self, key, value):
- """ Set the given environment key to the given value, overriding any
- previous value
- """
-
+ """Set the given environment key to the given value, overriding any previous value."""
if key == "Stack":
self._set_stack(value)
@@ -145,7 +145,7 @@ class Environment:
n = node.strip()
socket.gethostbyname_ex(n)
self._nodes.append(n)
- except:
+ except socket.herror:
self._logger.log("%s not found in DNS... aborting" % node)
raise
@@ -155,21 +155,18 @@ class Environment:
self.data[key] = value
def random_node(self):
- """ Choose a random node from the cluster """
-
+ """Choose a random node from the cluster."""
return self.random_gen.choice(self["nodes"])
def get(self, key, default=None):
- """ Return the value for key if key is in the environment, else default """
-
+ """Return the value for key if key is in the environment, else default."""
if key == "nodes":
return self._nodes
return self.data.get(key, default)
def _set_stack(self, name):
- """ Normalize the given cluster stack name """
-
+ """Normalize the given cluster stack name."""
if name in ["corosync", "cs", "mcp"]:
self.data["Stack"] = "corosync 2+"
@@ -177,8 +174,7 @@ class Environment:
raise ValueError("Unknown stack: %s" % name)
def _get_stack_short(self):
- """ Return the short name for the currently set cluster stack """
-
+ """Return the short name for the currently set cluster stack."""
if "Stack" not in self.data:
return "unknown"
@@ -189,15 +185,13 @@ class Environment:
raise ValueError("Unknown stack: %s" % self["stack"])
def _detect_systemd(self):
- """ Detect whether systemd is in use on the target node """
-
+ """Detect whether systemd is in use on the target node."""
if "have_systemd" not in self.data:
(rc, _) = self._rsh(self._target, "systemctl list-units", verbose=0)
self["have_systemd"] = rc == 0
def _detect_syslog(self):
- """ Detect the syslog variant in use on the target node """
-
+ """Detect the syslog variant in use on the target node."""
if "syslogd" not in self.data:
if self["have_systemd"]:
# Systemd
@@ -213,8 +207,7 @@ class Environment:
self["syslogd"] = "rsyslog"
def disable_service(self, node, service):
- """ Disable the given service on the given node """
-
+ """Disable the given service on the given node."""
if self["have_systemd"]:
# Systemd
(rc, _) = self._rsh(node, "systemctl disable %s" % service)
@@ -225,8 +218,7 @@ class Environment:
return rc
def enable_service(self, node, service):
- """ Enable the given service on the given node """
-
+ """Enable the given service on the given node."""
if self["have_systemd"]:
# Systemd
(rc, _) = self._rsh(node, "systemctl enable %s" % service)
@@ -237,8 +229,7 @@ class Environment:
return rc
def service_is_enabled(self, node, service):
- """ Is the given service enabled on the given node? """
-
+ """Return True if the given service is enabled on the given node."""
if self["have_systemd"]:
# Systemd
@@ -254,15 +245,13 @@ class Environment:
return rc == 0
def _detect_at_boot(self):
- """ Detect if the cluster starts at boot """
-
+ """Detect if the cluster starts at boot."""
if "at-boot" not in self.data:
self["at-boot"] = self.service_is_enabled(self._target, "corosync") \
- or self.service_is_enabled(self._target, "pacemaker")
+ or self.service_is_enabled(self._target, "pacemaker")
def _detect_ip_offset(self):
- """ Detect the offset for IPaddr resources """
-
+ """Detect the offset for IPaddr resources."""
if self["CIBResource"] and "IPBase" not in self.data:
(_, lines) = self._rsh(self._target, "ip addr | grep inet | grep -v -e link -e inet6 -e '/32' -e ' lo' | awk '{print $2}'", verbose=0)
network = lines[0].strip()
@@ -290,10 +279,12 @@ class Environment:
self._logger.log("Defaulting to '%s', use --test-ip-base to override" % self["IPBase"])
def _filter_nodes(self):
- """ If --limit-nodes is given, keep that many nodes from the front of the
- list of cluster nodes and drop the rest
"""
+ Filter the list of cluster nodes.
+ If --limit-nodes is given, keep that many nodes from the front of the
+ list of cluster nodes and drop the rest.
+ """
if self["node-limit"] > 0:
if len(self["nodes"]) > self["node-limit"]:
# pylint thinks self["node-limit"] is a list even though we initialize
@@ -303,17 +294,15 @@ class Environment:
% (len(self["nodes"]), self["node-limit"]))
while len(self["nodes"]) > self["node-limit"]:
- self["nodes"].pop(len(self["nodes"])-1)
+ self["nodes"].pop(len(self["nodes"]) - 1)
def _validate(self):
- """ Were we given all the required command line parameters? """
-
+ """Check that we were given all required command line parameters."""
if not self["nodes"]:
raise ValueError("No nodes specified!")
def _discover(self):
- """ Probe cluster nodes to figure out how to log and manage services """
-
+ """Probe cluster nodes to figure out how to log and manage services."""
self._target = random.Random().choice(self["nodes"])
exerciser = socket.gethostname()
@@ -332,11 +321,12 @@ class Environment:
self._detect_ip_offset()
def _parse_args(self, argv):
- """ Parse and validate command line parameters, setting the appropriate
- values in the environment dictionary. If argv is None, use sys.argv
- instead.
"""
+ Parse and validate command line parameters.
+ Set the appropriate values in the environment dictionary. If argv is
+ None, use sys.argv instead.
+ """
if not argv:
argv = sys.argv[1:]
@@ -525,10 +515,10 @@ class Environment:
with open(dsh_file, "r", encoding="utf-8") as f:
for line in f:
- l = line.strip()
+ stripped = line.strip()
- if not l.startswith('#'):
- self["nodes"].append(l)
+ if not stripped.startswith('#'):
+ self["nodes"].append(stripped)
else:
print("Unknown DSH group: %s" % args.dsh_group)
@@ -597,7 +587,7 @@ class Environment:
if args.logfile:
self["LogAuditDisabled"] = True
self["LogFileName"] = args.logfile
- self["LogWatcher"] = LogKind.REMOTE_FILE
+ self["log_kind"] = LogKind.REMOTE_FILE
else:
# We can't set this as the default on the parser.add_argument call
# for this option because then args.logfile will be set, which means
@@ -629,17 +619,19 @@ class Environment:
self[name] = value
print("Setting %s = %s" % (name, value))
+
class EnvFactory:
- """ A class for constructing a singleton instance of an Environment object """
+ """A class for constructing a singleton instance of an Environment object."""
instance = None
# pylint: disable=invalid-name
def getInstance(self, args=None):
- """ Returns the previously created instance of Environment, or creates a
- new instance if one does not already exist.
"""
+ Return the previously created instance of Environment.
+ If no instance exists, create a new instance and return that.
+ """
if not EnvFactory.instance:
EnvFactory.instance = Environment(args)
diff --git a/python/pacemaker/_cts/errors.py b/python/pacemaker/_cts/errors.py
index 2e245e7..a731640 100644
--- a/python/pacemaker/_cts/errors.py
+++ b/python/pacemaker/_cts/errors.py
@@ -1,53 +1,61 @@
-""" A module providing custom exception classes used throughout the pacemaker library """
+"""A module providing custom exception classes used throughout the pacemaker library."""
__all__ = ["ExitCodeError", "OutputFoundError", "OutputNotFoundError", "XmlValidationError"]
-__copyright__ = "Copyright 2009-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2009-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+)"
class TestError(Exception):
- """ Base class for exceptions in this module """
+ """Base class for exceptions in this module."""
class ExitCodeError(TestError):
- """ Exception raised when command exit status is unexpected """
+ """Exception raised when command exit status is unexpected."""
def __init__(self, exit_code):
+ """Create a new ExitCodeError instance."""
TestError.__init__(self)
self.exit_code = exit_code
def __str__(self):
+ """Return a printable string for this exception."""
return repr(self.exit_code)
class OutputNotFoundError(TestError):
- """ Exception raised when command output does not contain wanted string """
+ """Exception raised when command output does not contain wanted string."""
def __init__(self, output):
+ """Create a new OutputNotFoundError instance."""
TestError.__init__(self)
self.output = output
def __str__(self):
+ """Return a printable string for this exception."""
return repr(self.output)
class OutputFoundError(TestError):
- """ Exception raised when command output contains unwanted string """
+ """Exception raised when command output contains unwanted string."""
def __init__(self, output):
+ """Create a new OutputFoundError instance."""
TestError.__init__(self)
self.output = output
def __str__(self):
+ """Return a printable string for this exception."""
return repr(self.output)
class XmlValidationError(TestError):
- """ Exception raised when xmllint fails """
+ """Exception raised when xmllint fails."""
def __init__(self, output):
+ """Create a new XmlValidationError instance."""
TestError.__init__(self)
self.output = output
def __str__(self):
+ """Return a printable string for this exception."""
return repr(self.output)
diff --git a/python/pacemaker/_cts/input.py b/python/pacemaker/_cts/input.py
index 7e734f6..739d371 100644
--- a/python/pacemaker/_cts/input.py
+++ b/python/pacemaker/_cts/input.py
@@ -1,12 +1,12 @@
-""" User input related utilities for CTS """
+"""User input related utilities for CTS."""
__all__ = ["should_continue"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
-def should_continue(env):
- """ On failure, prompt the user to see if we should continue """
+def should_continue(env):
+ """On failure, prompt the user to see if we should continue."""
if env["continue"]:
return True
diff --git a/python/pacemaker/_cts/logging.py b/python/pacemaker/_cts/logging.py
index 6c7bfb0..de1ed05 100644
--- a/python/pacemaker/_cts/logging.py
+++ b/python/pacemaker/_cts/logging.py
@@ -1,7 +1,7 @@
-""" Logging classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Logging classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["LogFactory"]
-__copyright__ = "Copyright 2014-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2014-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import os
@@ -10,7 +10,7 @@ import time
class Logger:
- """ Abstract class to use as parent for CTS logging classes """
+ """Abstract class to use as parent for CTS logging classes."""
TimeFormat = "%b %d %H:%M:%S\t"
@@ -26,38 +26,33 @@ class Logger:
self._source = ""
def __call__(self, lines):
- """ Log specified messages """
-
+ """Log specified messages."""
raise ValueError("Abstract class member (__call__)")
def write(self, line):
- """ Log a single line excluding trailing whitespace """
-
+ """Log a single line excluding trailing whitespace."""
return self(line.rstrip())
def writelines(self, lines):
- """ Log a series of lines excluding trailing whitespace """
-
+ """Log a series of lines excluding trailing whitespace."""
for line in lines:
self.write(line)
@property
def is_debug_target(self):
- """ Return True if this logger should receive debug messages """
-
+ """Return True if this logger should receive debug messages."""
return self._debug_target
class StdErrLog(Logger):
- """ Class to log to standard error """
+ """Class to log to standard error."""
def __init__(self, filename, tag):
Logger.__init__(self, filename, tag)
self._debug_target = False
def __call__(self, lines):
- """ Log specified lines to stderr """
-
+ """Log specified lines to stderr."""
timestamp = time.strftime(Logger.TimeFormat,
time.localtime(time.time()))
if isinstance(lines, str):
@@ -70,15 +65,14 @@ class StdErrLog(Logger):
class FileLog(Logger):
- """ Class to log to a file """
+ """Class to log to a file."""
def __init__(self, filename, tag):
Logger.__init__(self, filename, tag)
self._hostname = os.uname()[1]
def __call__(self, lines):
- """ Log specified lines to the file """
-
+ """Log specified lines to the file."""
with open(self._logfile, "at", encoding="utf-8") as logf:
timestamp = time.strftime(Logger.TimeFormat,
time.localtime(time.time()))
@@ -92,39 +86,34 @@ class FileLog(Logger):
class LogFactory:
- """ Singleton to log messages to various destinations """
+ """Singleton to log messages to various destinations."""
log_methods = []
have_stderr = False
def add_file(self, filename, tag=None):
- """ When logging messages, log them to specified file """
-
+ """When logging messages, log them to specified file."""
if filename:
LogFactory.log_methods.append(FileLog(filename, tag))
def add_stderr(self):
- """ When logging messages, log them to standard error """
-
+ """When logging messages, log them to standard error."""
if not LogFactory.have_stderr:
LogFactory.have_stderr = True
LogFactory.log_methods.append(StdErrLog(None, None))
def log(self, args):
- """ Log a message (to all configured log destinations) """
-
+ """Log a message (to all configured log destinations)."""
for logfn in LogFactory.log_methods:
logfn(args.strip())
def debug(self, args):
- """ Log a debug message (to all configured log destinations) """
-
+ """Log a debug message (to all configured log destinations)."""
for logfn in LogFactory.log_methods:
if logfn.is_debug_target:
logfn("debug: %s" % args.strip())
def traceback(self, traceback):
- """ Log a stack trace (to all configured log destinations) """
-
+ """Log a stack trace (to all configured log destinations)."""
for logfn in LogFactory.log_methods:
traceback.print_exc(50, logfn)
diff --git a/python/pacemaker/_cts/network.py b/python/pacemaker/_cts/network.py
index 33e401f..6ba776c 100644
--- a/python/pacemaker/_cts/network.py
+++ b/python/pacemaker/_cts/network.py
@@ -1,29 +1,29 @@
-""" Network related utilities for CTS """
+"""Network related utilities for CTS."""
__all__ = ["next_ip"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
# pylint: disable=global-statement
CURRENT_IP = None
-def next_ip(ip_base=None, reset=False):
- """ Return the next available IP address.
-
- Arguments:
-
- ip_base -- The initial IP address to start from. The first call to next_ip
- will return the next IP address from this base. Each subsequent
- call will return the next address from the previous call, so you
- can just omit this argument for subsequent calls.
- reset -- Force next_ip to start from ip_base again. This requires also
- passing the ip_base argument. (Mostly useful for unit testing,
- but may be useful elsewhere).
- This function only increments the last portion of the IP address. Once it
- has hit the upper limit, ValueError will be raised.
+def next_ip(ip_base=None, reset=False):
+ """
+ Return the next available IP address.
+
+ This function only increments the last portion of the IP address. Once it
+ has hit the upper limit, ValueError will be raised.
+
+ Arguments:
+ ip_base -- The initial IP address to start from. The first call to next_ip
+ will return the next IP address from this base. Each subsequent
+ call will return the next address from the previous call, so you
+ can just omit this argument for subsequent calls.
+ reset -- Force next_ip to start from ip_base again. This requires also
+ passing the ip_base argument. (Mostly useful for unit testing,
+ but may be useful elsewhere).
"""
-
global CURRENT_IP
if CURRENT_IP is None or reset:
diff --git a/python/pacemaker/_cts/patterns.py b/python/pacemaker/_cts/patterns.py
index 0fb1c2b..333eac4 100644
--- a/python/pacemaker/_cts/patterns.py
+++ b/python/pacemaker/_cts/patterns.py
@@ -1,24 +1,23 @@
-""" Pattern-holding classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Pattern-holding classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["PatternSelector"]
-__copyright__ = "Copyright 2008-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2008-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+)"
import argparse
from pacemaker.buildoptions import BuildOptions
+
class BasePatterns:
- """ The base class for holding a stack-specific set of command and log
- file/stdout patterns. Stack-specific classes need to be built on top
- of this one.
"""
+ The base class for holding a stack-specific set of command and log file/stdout patterns.
- def __init__(self):
- """ Create a new BasePatterns instance which holds a very minimal set of
- basic patterns.
- """
+ Stack-specific classes need to be built on top of this one.
+ """
+ def __init__(self):
+ """Create a new BasePatterns instance which holds a very minimal set of basic patterns."""
self._bad_news = []
self._components = {}
self._name = "crm-base"
@@ -32,60 +31,67 @@ class BasePatterns:
# pcs can log this when node is fenced, but fencing is OK in some
# tests (and we will catch it in pacemaker logs when not OK)
r"pcs.daemon:No response from: .* request: get_configs, error:",
+
+ # This is overbroad, but there's no way to say that only certain
+ # transition errors are acceptable. We have to rely on causes of a
+ # transition error logging their own error message, which should
+ # always be the case.
+ r"pacemaker-schedulerd.* Calculated transition .*/pe-error",
]
self._commands = {
- "StatusCmd" : "crmadmin -t 60 -S %s 2>/dev/null",
- "CibQuery" : "cibadmin -Ql",
- "CibAddXml" : "cibadmin --modify -c --xml-text %s",
- "CibDelXpath" : "cibadmin --delete --xpath %s",
- "RscRunning" : BuildOptions.DAEMON_DIR + "/cts-exec-helper -R -r %s",
- "CIBfile" : "%s:" + BuildOptions.CIB_DIR + "/cib.xml",
- "TmpDir" : "/tmp",
-
- "BreakCommCmd" : "iptables -A INPUT -s %s -j DROP >/dev/null 2>&1",
- "FixCommCmd" : "iptables -D INPUT -s %s -j DROP >/dev/null 2>&1",
-
- "MaintenanceModeOn" : "cibadmin --modify -c --xml-text '<cluster_property_set id=\"cib-bootstrap-options\"><nvpair id=\"cts-maintenance-mode-setting\" name=\"maintenance-mode\" value=\"true\"/></cluster_property_set>'",
- "MaintenanceModeOff" : "cibadmin --delete --xpath \"//nvpair[@name='maintenance-mode']\"",
-
- "StandbyCmd" : "crm_attribute -Vq -U %s -n standby -l forever -v %s 2>/dev/null",
- "StandbyQueryCmd" : "crm_attribute -qG -U %s -n standby -l forever -d off 2>/dev/null",
+ "StatusCmd": "crmadmin -t 60 -S %s 2>/dev/null",
+ "CibQuery": "cibadmin -Ql",
+ "CibAddXml": "cibadmin --modify -c --xml-text %s",
+ "CibDelXpath": "cibadmin --delete --xpath %s",
+ "RscRunning": BuildOptions.DAEMON_DIR + "/cts-exec-helper -R -r %s",
+ "CIBfile": "%s:" + BuildOptions.CIB_DIR + "/cib.xml",
+ "TmpDir": "/tmp",
+
+ "BreakCommCmd": "iptables -A INPUT -s %s -j DROP >/dev/null 2>&1",
+ "FixCommCmd": "iptables -D INPUT -s %s -j DROP >/dev/null 2>&1",
+
+ "MaintenanceModeOn": "cibadmin --modify -c --xml-text '<cluster_property_set id=\"cib-bootstrap-options\"><nvpair id=\"cts-maintenance-mode-setting\" name=\"maintenance-mode\" value=\"true\"/></cluster_property_set>'",
+ "MaintenanceModeOff": "cibadmin --delete --xpath \"//nvpair[@name='maintenance-mode']\"",
+
+ "StandbyCmd": "crm_attribute -Vq -U %s -n standby -l forever -v %s 2>/dev/null",
+ "StandbyQueryCmd": "crm_attribute -qG -U %s -n standby -l forever -d off 2>/dev/null",
}
self._search = {
- "Pat:DC_IDLE" : r"pacemaker-controld.*State transition.*-> S_IDLE",
+ "Pat:DC_IDLE": r"pacemaker-controld.*State transition.*-> S_IDLE",
# This won't work if we have multiple partitions
- "Pat:Local_started" : r"%s\W.*controller successfully started",
- "Pat:NonDC_started" : r"%s\W.*State transition.*-> S_NOT_DC",
- "Pat:DC_started" : r"%s\W.*State transition.*-> S_IDLE",
- "Pat:We_stopped" : r"%s\W.*OVERRIDE THIS PATTERN",
- "Pat:They_stopped" : r"%s\W.*LOST:.* %s ",
- "Pat:They_dead" : r"node %s.*: is dead",
- "Pat:They_up" : r"%s %s\W.*OVERRIDE THIS PATTERN",
- "Pat:TransitionComplete" : "Transition status: Complete: complete",
-
- "Pat:Fencing_start" : r"Requesting peer fencing .* targeting %s",
- "Pat:Fencing_ok" : r"pacemaker-fenced.*:\s*Operation .* targeting %s by .* for .*@.*: OK",
- "Pat:Fencing_recover" : r"pacemaker-schedulerd.*: Recover\s+%s",
- "Pat:Fencing_active" : r"stonith resource .* is active on 2 nodes (attempting recovery)",
- "Pat:Fencing_probe" : r"pacemaker-controld.* Result of probe operation for %s on .*: Error",
-
- "Pat:RscOpOK" : r"pacemaker-controld.*:\s+Result of %s operation for %s.*: (0 \()?ok",
- "Pat:RscOpFail" : r"pacemaker-schedulerd.*:.*Unexpected result .* recorded for %s of %s ",
- "Pat:CloneOpFail" : r"pacemaker-schedulerd.*:.*Unexpected result .* recorded for %s of (%s|%s) ",
- "Pat:RscRemoteOpOK" : r"pacemaker-controld.*:\s+Result of %s operation for %s on %s: (0 \()?ok",
- "Pat:NodeFenced" : r"pacemaker-controld.*:\s* Peer %s was terminated \(.*\) by .* on behalf of .*: OK",
+ "Pat:Local_started": r"%s\W.*controller successfully started",
+ "Pat:NonDC_started": r"%s\W.*State transition.*-> S_NOT_DC",
+ "Pat:DC_started": r"%s\W.*State transition.*-> S_IDLE",
+ "Pat:We_stopped": r"%s\W.*OVERRIDE THIS PATTERN",
+ "Pat:They_stopped": r"%s\W.*LOST:.* %s ",
+ "Pat:They_dead": r"node %s.*: is dead",
+ "Pat:They_up": r"%s %s\W.*OVERRIDE THIS PATTERN",
+ "Pat:TransitionComplete": "Transition status: Complete: complete",
+
+ "Pat:Fencing_start": r"Requesting peer fencing .* targeting %s",
+ "Pat:Fencing_ok": r"pacemaker-fenced.*:\s*Operation .* targeting %s by .* for .*@.*: OK",
+ "Pat:Fencing_recover": r"pacemaker-schedulerd.*: Recover\s+%s",
+ "Pat:Fencing_active": r"stonith resource .* is active on 2 nodes (attempting recovery)",
+ "Pat:Fencing_probe": r"pacemaker-controld.* Result of probe operation for %s on .*: Error",
+
+ "Pat:RscOpOK": r"pacemaker-controld.*:\s+Result of %s operation for %s.*: (0 \()?ok",
+ "Pat:RscOpFail": r"pacemaker-schedulerd.*:.*Unexpected result .* recorded for %s of %s ",
+ "Pat:CloneOpFail": r"pacemaker-schedulerd.*:.*Unexpected result .* recorded for %s of (%s|%s) ",
+ "Pat:RscRemoteOpOK": r"pacemaker-controld.*:\s+Result of %s operation for %s on %s: (0 \()?ok",
+ "Pat:NodeFenced": r"pacemaker-controld.*:\s* Peer %s was terminated \(.*\) by .* on behalf of .*: OK",
}
def get_component(self, key):
- """ Return the patterns for a single component as a list, given by key.
- This is typically the name of some subprogram (pacemaker-based,
- pacemaker-fenced, etc.) or various special purpose keys. If key is
- unknown, return an empty list.
"""
+ Return the patterns for a single component as a list, given by key.
+ This is typically the name of some subprogram (pacemaker-based,
+ pacemaker-fenced, etc.) or various special purpose keys. If key is
+ unknown, return an empty list.
+ """
if key in self._components:
return self._components[key]
@@ -93,11 +99,12 @@ class BasePatterns:
return []
def get_patterns(self, key):
- """ Return various patterns supported by this object, given by key.
- Depending on the key, this could either be a list or a hash. If key
- is unknown, return None.
"""
+ Return various patterns supported by this object, given by key.
+ Depending on the key, this could either be a list or a hash. If key is
+ unknown, return None.
+ """
if key == "BadNews":
return self._bad_news
if key == "BadNewsIgnore":
@@ -125,36 +132,36 @@ class BasePatterns:
class Corosync2Patterns(BasePatterns):
- """ Patterns for Corosync version 2 cluster manager class """
+ """Patterns for Corosync version 2 cluster manager class."""
def __init__(self):
BasePatterns.__init__(self)
self._name = "crm-corosync"
self._commands.update({
- "StartCmd" : "service corosync start && service pacemaker start",
- "StopCmd" : "service pacemaker stop; [ ! -e /usr/sbin/pacemaker-remoted ] || service pacemaker_remote stop; service corosync stop",
+ "StartCmd": "service corosync start && service pacemaker start",
+ "StopCmd": "service pacemaker stop; [ ! -e /usr/sbin/pacemaker-remoted ] || service pacemaker_remote stop; service corosync stop",
- "EpochCmd" : "crm_node -e",
- "QuorumCmd" : "crm_node -q",
- "PartitionCmd" : "crm_node -p",
+ "EpochCmd": "crm_node -e",
+ "QuorumCmd": "crm_node -q",
+ "PartitionCmd": "crm_node -p",
})
self._search.update({
# Close enough ... "Corosync Cluster Engine exiting normally" isn't
# printed reliably.
- "Pat:We_stopped" : r"%s\W.*Unloading all Corosync service engines",
- "Pat:They_stopped" : r"%s\W.*pacemaker-controld.*Node %s(\[|\s).*state is now lost",
- "Pat:They_dead" : r"pacemaker-controld.*Node %s(\[|\s).*state is now lost",
- "Pat:They_up" : r"\W%s\W.*pacemaker-controld.*Node %s state is now member",
+ "Pat:We_stopped": r"%s\W.*Unloading all Corosync service engines",
+ "Pat:They_stopped": r"%s\W.*pacemaker-controld.*Node %s(\[|\s).*state is now lost",
+ "Pat:They_dead": r"pacemaker-controld.*Node %s(\[|\s).*state is now lost",
+ "Pat:They_up": r"\W%s\W.*pacemaker-controld.*Node %s state is now member",
- "Pat:ChildExit" : r"\[[0-9]+\] exited with status [0-9]+ \(",
+ "Pat:ChildExit": r"\[[0-9]+\] exited with status [0-9]+ \(",
# "with signal 9" == pcmk_child_exit(), "$" == check_active_before_startup_processes()
- "Pat:ChildKilled" : r"%s\W.*pacemakerd.*%s\[[0-9]+\] terminated( with signal 9|$)",
- "Pat:ChildRespawn" : r"%s\W.*pacemakerd.*Respawning %s subdaemon after unexpected exit",
+ "Pat:ChildKilled": r"%s\W.*pacemakerd.*%s\[[0-9]+\] terminated( with signal 9|$)",
+ "Pat:ChildRespawn": r"%s\W.*pacemakerd.*Respawning %s subdaemon after unexpected exit",
- "Pat:InfraUp" : r"%s\W.*corosync.*Initializing transport",
- "Pat:PacemakerUp" : r"%s\W.*pacemakerd.*Starting Pacemaker",
+ "Pat:InfraUp": r"%s\W.*corosync.*Initializing transport",
+ "Pat:PacemakerUp": r"%s\W.*pacemakerd.*Starting Pacemaker",
})
self._ignore += [
@@ -239,13 +246,7 @@ class Corosync2Patterns(BasePatterns):
r"error:.*cib_(shm|rw) IPC provider disconnected while waiting",
r"error:.*Connection to (fencer|stonith-ng).* (closed|failed|lost)",
r"error: Lost fencer connection",
- # This is overbroad, but we don't have a way to say that only
- # certain transition errors are acceptable (if the fencer respawns,
- # fence devices may appear multiply active). We have to rely on
- # other causes of a transition error logging their own error
- # message, which is the usual practice.
- r"pacemaker-schedulerd.* Calculated transition .*/pe-error",
- ]
+ ]
self._components["corosync"] = [
# We expect each daemon to lose its cluster connection.
@@ -281,12 +282,6 @@ class Corosync2Patterns(BasePatterns):
r"pacemaker-execd.*Connection to (fencer|stonith-ng).* (closed|failed|lost)",
r"pacemaker-controld.*:\s+Result of .* operation for Fencing.*Error \(Lost connection to fencer\)",
r"pacemaker-controld.*:Could not connect to attrd: Connection refused",
- # This is overbroad, but we don't have a way to say that only
- # certain transition errors are acceptable (if the fencer respawns,
- # fence devices may appear multiply active). We have to rely on
- # other causes of a transition error logging their own error
- # message, which is the usual practice.
- r"pacemaker-schedulerd.* Calculated transition .*/pe-error",
]
self._components["pacemaker-execd"] = [
@@ -338,12 +333,6 @@ class Corosync2Patterns(BasePatterns):
r"error:.*Lost fencer connection",
r"error:.*Fencer connection failed \(will retry\)",
r"pacemaker-controld.*:\s+Result of .* operation for Fencing.*Error \(Lost connection to fencer\)",
- # This is overbroad, but we don't have a way to say that only
- # certain transition errors are acceptable (if the fencer respawns,
- # fence devices may appear multiply active). We have to rely on
- # other causes of a transition error logging their own error
- # message, which is the usual practice.
- r"pacemaker-schedulerd.* Calculated transition .*/pe-error",
]
self._components["pacemaker-fenced-ignore"].extend(self._components["common-ignore"])
@@ -356,17 +345,16 @@ patternVariants = {
class PatternSelector:
- """ A class for choosing one of several Pattern objects and then extracting
- various pieces of information from that object
- """
+ """Choose from among several Pattern objects and return the information from that object."""
def __init__(self, name="crm-corosync"):
- """ Create a new PatternSelector object by instantiating whatever class
- is given by name. Defaults to Corosync2Patterns for "crm-corosync" or
- None. While other objects could be supported in the future, only this
- and the base object are supported at this time.
"""
+ Create a new PatternSelector object.
+ Instantiate whatever class is given by name. Defaults to Corosync2Patterns
+ for "crm-corosync" or None. While other objects could be supported in the
+ future, only this and the base object are supported at this time.
+ """
self._name = name
# If no name was given, use the default. Otherwise, look up the appropriate
@@ -377,23 +365,23 @@ class PatternSelector:
self._base = patternVariants[name]()
def get_patterns(self, kind):
- """ Call get_patterns on the previously instantiated pattern object """
-
+ """Call get_patterns on the previously instantiated pattern object."""
return self._base.get_patterns(kind)
def get_template(self, key):
- """ Return a single pattern from the previously instantiated pattern
- object as a string, or None if no pattern exists for the given key.
"""
+ Return a single pattern from the previously instantiated pattern object.
+ If no pattern exists for the given key, return None.
+ """
return self._base[key]
def get_component(self, kind):
- """ Call get_component on the previously instantiated pattern object """
-
+ """Call get_component on the previously instantiated pattern object."""
return self._base.get_component(kind)
def __getitem__(self, key):
+ """Return the pattern for the given key, or None if it does not exist."""
return self.get_template(key)
diff --git a/python/pacemaker/_cts/process.py b/python/pacemaker/_cts/process.py
index 757360c..c25ce33 100644
--- a/python/pacemaker/_cts/process.py
+++ b/python/pacemaker/_cts/process.py
@@ -1,4 +1,4 @@
-""" A module for managing and communicating with external processes """
+"""A module for managing and communicating with external processes."""
__all__ = ["killall", "exit_if_proc_running", "pipe_communicate", "stdout_from_command"]
__copyright__ = "Copyright 2009-2023 the Pacemaker project contributors"
@@ -11,9 +11,9 @@ import psutil
from pacemaker.exitstatus import ExitStatus
-def killall(process_names, terminate=False):
- """ Kill all instances of every process in a list """
+def killall(process_names, terminate=False):
+ """Kill all instances of every process in a list."""
if not process_names:
return
@@ -36,8 +36,7 @@ def killall(process_names, terminate=False):
def is_proc_running(process_name):
- """ Check whether a process with a given name is running """
-
+ """Check whether a process with a given name is running."""
for proc in psutil.process_iter(["name"]):
if proc.info["name"] == process_name:
return True
@@ -45,8 +44,7 @@ def is_proc_running(process_name):
def exit_if_proc_running(process_name):
- """ Exit with error if a given process is running """
-
+ """Exit with error if a given process is running."""
if is_proc_running(process_name):
print("Error: %s is already running!" % process_name)
print("Run %s only when the cluster is stopped." % sys.argv[0])
@@ -54,8 +52,7 @@ def exit_if_proc_running(process_name):
def pipe_communicate(pipes, check_stderr=False, stdin=None):
- """ Get text output from pipes """
-
+ """Get text output from pipes."""
if stdin is not None:
pipe_outputs = pipes.communicate(input=stdin.encode())
else:
@@ -69,8 +66,7 @@ def pipe_communicate(pipes, check_stderr=False, stdin=None):
def stdout_from_command(args):
- """ Execute command and return its standard output """
-
+ """Execute command and return its standard output."""
with subprocess.Popen(args, stdout=subprocess.PIPE) as p:
p.wait()
return pipe_communicate(p).split("\n")
diff --git a/python/pacemaker/_cts/remote.py b/python/pacemaker/_cts/remote.py
index 4b6b8f6..ba5b878 100644
--- a/python/pacemaker/_cts/remote.py
+++ b/python/pacemaker/_cts/remote.py
@@ -1,7 +1,7 @@
-""" Remote command runner for Pacemaker's Cluster Test Suite (CTS) """
+"""Remote command runner for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["RemoteExec", "RemoteFactory"]
-__copyright__ = "Copyright 2014-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2014-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -12,11 +12,14 @@ from threading import Thread
from pacemaker._cts.logging import LogFactory
+
def convert2string(lines):
- """ Convert a byte string to a UTF-8 string, and a list of byte strings to
- a list of UTF-8 strings. All other text formats are passed through.
"""
+ Convert byte strings to UTF-8 strings.
+ Lists of byte strings are converted to a list of UTF-8 strings. All other
+ text formats are passed through.
+ """
if isinstance(lines, bytes):
return lines.decode("utf-8")
@@ -32,23 +35,23 @@ def convert2string(lines):
return lines
+
class AsyncCmd(Thread):
- """ A class for doing the hard work of running a command on another machine """
+ """A class for doing the hard work of running a command on another machine."""
def __init__(self, node, command, proc=None, delegate=None):
- """ Create a new AsyncCmd instance
-
- Arguments:
-
- node -- The remote machine to run on
- command -- The ssh command string to use for remote execution
- proc -- If not None, a process object previously created with Popen.
- Instead of spawning a new process, we will then wait on
- this process to finish and handle its output.
- delegate -- When the command completes, call the async_complete method
- on this object
"""
-
+ Create a new AsyncCmd instance.
+
+ Arguments:
+ node -- The remote machine to run on
+ command -- The ssh command string to use for remote execution
+ proc -- If not None, a process object previously created with Popen.
+ Instead of spawning a new process, we will then wait on
+ this process to finish and handle its output.
+ delegate -- When the command completes, call the async_complete method
+ on this object
+ """
self._command = command
self._delegate = delegate
self._logger = LogFactory()
@@ -58,8 +61,7 @@ class AsyncCmd(Thread):
Thread.__init__(self)
def run(self):
- """ Run the previously instantiated AsyncCmd object """
-
+ """Run the previously instantiated AsyncCmd object."""
out = None
err = None
@@ -92,21 +94,23 @@ class AsyncCmd(Thread):
if self._delegate:
self._delegate.async_complete(self._proc.pid, self._proc.returncode, out, err)
+
class RemoteExec:
- """ An abstract class for remote execution. It runs a command on another
- machine using ssh and scp.
"""
+ An abstract class for remote execution.
- def __init__(self, command, cp_command, silent=False):
- """ Create a new RemoteExec instance
-
- Arguments:
+ It runs a command on another machine using ssh and scp.
+ """
- command -- The ssh command string to use for remote execution
- cp_command -- The scp command string to use for copying files
- silent -- Should we log command status?
+ def __init__(self, command, cp_command, silent=False):
"""
+ Create a new RemoteExec instance.
+ Arguments:
+ command -- The ssh command string to use for remote execution
+ cp_command -- The scp command string to use for copying files
+ silent -- Should we log command status?
+ """
self._command = command
self._cp_command = cp_command
self._logger = LogFactory()
@@ -114,15 +118,11 @@ class RemoteExec:
self._our_node = os.uname()[1].lower()
def _fixcmd(self, cmd):
- """ Perform shell escapes on certain characters in the input cmd string """
-
+ """Perform shell escapes on certain characters in the input cmd string."""
return re.sub("\'", "'\\''", cmd)
def _cmd(self, args):
- """ Given a list of arguments, return the string that will be run on the
- remote system
- """
-
+ """Given a list of arguments, return the string that will be run on the remote system."""
sysname = args[0]
command = args[1]
@@ -134,56 +134,48 @@ class RemoteExec:
return ret
def _log(self, args):
- """ Log a message """
-
+ """Log a message."""
if not self._silent:
self._logger.log(args)
def _debug(self, args):
- """ Log a message at the debug level """
-
+ """Log a message at the debug level."""
if not self._silent:
self._logger.debug(args)
def call_async(self, node, command, delegate=None):
- """ Run the given command on the given remote system and do not wait for
- it to complete.
-
- Arguments:
-
- node -- The remote machine to run on
- command -- The command to run, as a string
- delegate -- When the command completes, call the async_complete method
- on this object
+ """
+ Run the given command on the given remote system and do not wait for it to complete.
- Returns:
+ Arguments:
+ node -- The remote machine to run on
+ command -- The command to run, as a string
+ delegate -- When the command completes, call the async_complete method
+ on this object
- The running process object
+ Returns the running process object.
"""
-
aproc = AsyncCmd(node, self._cmd([node, command]), delegate=delegate)
aproc.start()
return aproc
def __call__(self, node, command, synchronous=True, verbose=2):
- """ Run the given command on the given remote system. If you call this class
- like a function, this is what gets called. It's approximately the same
- as a system() call on the remote machine.
-
- Arguments:
+ """
+ Run the given command on the given remote system.
- node -- The remote machine to run on
- command -- The command to run, as a string
- synchronous -- Should we wait for the command to complete?
- verbose -- If 0, do not lo:g anything. If 1, log the command and its
- return code but not its output. If 2, additionally log
- command output.
+ If you call this class like a function, this is what gets called. It's
+ approximately the same as a system() call on the remote machine.
- Returns:
+ Arguments:
+ node -- The remote machine to run on
+ command -- The command to run, as a string
+ synchronous -- Should we wait for the command to complete?
+ verbose -- If 0, do not lo:g anything. If 1, log the command and its
+ return code but not its output. If 2, additionally log
+ command output.
- A tuple of (return code, command output)
+ Returns a tuple of (return code, command output).
"""
-
rc = 0
result = None
# pylint: disable=consider-using-with
@@ -222,14 +214,14 @@ class RemoteExec:
return (rc, result)
def copy(self, source, target, silent=False):
- """ Perform a copy of the source file to the remote target, using the
- cp_command provided when the RemoteExec object was created.
+ """
+ Perform a copy of the source file to the remote target.
- Returns:
+ This function uses the cp_command provided when the RemoteExec object
+ was created.
- The return code of the cp_command
+ Returns the return code of the cp_command.
"""
-
cmd = "%s '%s' '%s'" % (self._cp_command, source, target)
rc = os.system(cmd)
@@ -239,8 +231,7 @@ class RemoteExec:
return rc
def exists_on_all(self, filename, hosts):
- """ Return True if specified file exists on all specified hosts. """
-
+ """Return True if specified file exists on all specified hosts."""
for host in hosts:
rc = self(host, "test -r %s" % filename)
if rc != 0:
@@ -250,7 +241,7 @@ class RemoteExec:
class RemoteFactory:
- """ A class for constructing a singleton instance of a RemoteExec object """
+ """A class for constructing a singleton instance of a RemoteExec object."""
# Class variables
@@ -268,10 +259,11 @@ class RemoteFactory:
# pylint: disable=invalid-name
def getInstance(self):
- """ Returns the previously created instance of RemoteExec, or creates a
- new instance if one does not already exist.
"""
+ Return the previously created instance of RemoteExec.
+ If no instance exists, create one and then return that.
+ """
if not RemoteFactory.instance:
RemoteFactory.instance = RemoteExec(RemoteFactory.command,
RemoteFactory.cp_command,
@@ -279,8 +271,7 @@ class RemoteFactory:
return RemoteFactory.instance
def enable_qarsh(self):
- """ Enable the QA remote shell """
-
+ """Enable the QA remote shell."""
# http://nstraz.wordpress.com/2008/12/03/introducing-qarsh/
print("Using QARSH for connections to cluster nodes")
diff --git a/python/pacemaker/_cts/scenarios.py b/python/pacemaker/_cts/scenarios.py
index 769b2d0..81e9e40 100644
--- a/python/pacemaker/_cts/scenarios.py
+++ b/python/pacemaker/_cts/scenarios.py
@@ -1,4 +1,4 @@
-""" Test scenario classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Test scenario classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = [
"AllOnce",
@@ -8,7 +8,7 @@ __all__ = [
"RandomTests",
"Sequence",
]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -19,66 +19,72 @@ from pacemaker._cts.input import should_continue
from pacemaker._cts.tests.ctstest import CTSTest
from pacemaker._cts.watcher import LogWatcher
+
class ScenarioComponent:
- """ The base class for all scenario components. A scenario component is
- one single step in a scenario. Each component is basically just a setup
- and teardown method.
"""
+ The base class for all scenario components.
- def __init__(self, cm, env):
- """ Create a new ScenarioComponent instance
-
- Arguments:
+ A scenario component is one single step in a scenario. Each component is
+ basically just a setup and teardown method.
+ """
- cm -- A ClusterManager instance
- env -- An Environment instance
+ def __init__(self, cm, env):
"""
+ Create a new ScenarioComponent instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ env -- An Environment instance
+ """
# pylint: disable=invalid-name
self._cm = cm
self._env = env
def is_applicable(self):
- """ Return True if this component is applicable in the given Environment.
- This method must be provided by all subclasses.
"""
+ Return True if this component is applicable in the given Environment.
+ This method must be provided by all subclasses.
+ """
raise NotImplementedError
def setup(self):
- """ Set up the component, returning True on success. This method must be
- provided by all subclasses.
"""
+ Set up the component, returning True on success.
+ This method must be provided by all subclasses.
+ """
raise NotImplementedError
def teardown(self):
- """ Tear down the given component. This method must be provided by all
- subclasses.
"""
+ Tear down the given component.
+ This method must be provided by all subclasses.
+ """
raise NotImplementedError
class Scenario:
- """ The base class for scenario. A scenario is an ordered list of
- ScenarioComponent objects. A scenario proceeds by setting up all its
- components in sequence, running a list of tests and audits, and then
- tearing down its components in reverse.
"""
+ The base class for scenarios.
- def __init__(self, cm, components, audits, tests):
- """ Create a new Scenario instance
-
- Arguments:
+ A scenario is an ordered list of ScenarioComponent objects. A scenario
+ proceeds by setting up all its components in sequence, running a list of
+ tests and audits, and then tearing down its components in reverse.
+ """
- cm -- A ClusterManager instance
- components -- A list of ScenarioComponents comprising this Scenario
- audits -- A list of ClusterAudits that will be performed as
- part of this Scenario
- tests -- A list of CTSTests that will be run
+ def __init__(self, cm, components, audits, tests):
+ """
+ Create a new Scenario instance.
+
+ Arguments:
+ cm -- A ClusterManager instance
+ components -- A list of ScenarioComponents comprising this Scenario
+ audits -- A list of ClusterAudits that will be performed as
+ part of this Scenario
+ tests -- A list of CTSTests that will be run
"""
-
# pylint: disable=invalid-name
self.stats = {
@@ -107,8 +113,7 @@ class Scenario:
raise ValueError("Init value must be a subclass of CTSTest")
def is_applicable(self):
- """ Return True if all ScenarioComponents are applicable """
-
+ """Return True if all ScenarioComponents are applicable."""
for comp in self._components:
if not comp.is_applicable():
return False
@@ -116,12 +121,14 @@ class Scenario:
return True
def setup(self):
- """ Set up the scenario, returning True on success. If setup fails at
- some point, tear down those components that did successfully set up.
"""
+ Set up the scenario, returning True on success.
+ If setup fails at some point, tear down those components that did
+ successfully set up.
+ """
self._cm.prepare()
- self.audit() # Also detects remote/local log config
+ self.audit() # Also detects remote/local log config
self._cm.ns.wait_for_all_nodes(self._cm.env["nodes"])
self.audit()
@@ -130,9 +137,9 @@ class Scenario:
self._bad_news = LogWatcher(self._cm.env["LogFileName"],
self._cm.templates.get_patterns("BadNews"),
self._cm.env["nodes"],
- self._cm.env["LogWatcher"],
+ self._cm.env["log_kind"],
"BadNews", 0)
- self._bad_news.set_watch() # Call after we've figured out what type of log watching to do in LogAudit
+ self._bad_news.set_watch() # Call after we've figured out what type of log watching to do in LogAudit
j = 0
while j < len(self._components):
@@ -149,12 +156,13 @@ class Scenario:
return True
def teardown(self, n_components=None):
- """ Tear down the scenario in the reverse order it was set up. If
- n_components is not None, only tear down that many components.
"""
+ Tear down the scenario in the reverse order it was set up.
+ If n_components is not None, only tear down that many components.
+ """
if not n_components:
- n_components = len(self._components)-1
+ n_components = len(self._components) - 1
j = n_components
@@ -166,37 +174,34 @@ class Scenario:
self._cm.install_support("uninstall")
def incr(self, name):
- """ Increment the given stats key """
-
- if not name in self.stats:
+ """Increment the given stats key."""
+ if name not in self.stats:
self.stats[name] = 0
self.stats[name] += 1
def run(self, iterations):
- """ Run all tests in the scenario the given number of times """
-
+ """Run all tests in the scenario the given number of times."""
self._cm.oprofile_start()
try:
self._run_loop(iterations)
self._cm.oprofile_stop()
- except:
+ except: # noqa: E722
self._cm.oprofile_stop()
raise
def _run_loop(self, iterations):
- """ Do the hard part of the run method - actually run all the tests the
- given number of times.
- """
-
+ """Run all the tests the given number of times."""
raise NotImplementedError
def run_test(self, test, testcount):
- """ Run the given test. testcount is the number of tests (including
- this one) that have been run across all iterations.
"""
+ Run the given test.
+ testcount is the number of tests (including this one) that have been
+ run across all iterations.
+ """
nodechoice = self._cm.env.random_node()
ret = True
@@ -254,8 +259,7 @@ class Scenario:
return did_run
def summarize(self):
- """ Output scenario results """
-
+ """Output scenario results."""
self._cm.log("****************")
self._cm.log("Overall Results:%r" % self.stats)
self._cm.log("****************")
@@ -283,11 +287,12 @@ class Scenario:
self._cm.log("<<<<<<<<<<<<<<<< TESTS COMPLETED")
def audit(self, local_ignore=None):
- """ Perform all scenario audits and log results. If there are too many
- failures, prompt the user to confirm that the scenario should continue
- running.
"""
+ Perform all scenario audits and log results.
+ If there are too many failures, prompt the user to confirm that the
+ scenario should continue running.
+ """
errcount = 0
ignorelist = ["CTS:"]
@@ -340,7 +345,7 @@ class Scenario:
class AllOnce(Scenario):
- """ Every Test Once """
+ """Every Test Once."""
def _run_loop(self, iterations):
testcount = 1
@@ -351,7 +356,7 @@ class AllOnce(Scenario):
class RandomTests(Scenario):
- """ Random Test Execution """
+ """Random Test Execution."""
def _run_loop(self, iterations):
testcount = 1
@@ -363,7 +368,7 @@ class RandomTests(Scenario):
class Sequence(Scenario):
- """ Named Tests in Sequence """
+ """Named Tests in Sequence."""
def _run_loop(self, iterations):
testcount = 1
@@ -375,26 +380,27 @@ class Sequence(Scenario):
class Boot(Scenario):
- """ Start the Cluster """
+ """Start the Cluster."""
def _run_loop(self, iterations):
return
class BootCluster(ScenarioComponent):
- """ The BootCluster component simply starts the cluster manager on all
- nodes, waiting for each to come up before starting given that a node
- might have been rebooted or crashed beforehand.
"""
+ Start the cluster manager on all nodes.
- def is_applicable(self):
- """ BootCluster is always applicable """
+ Wait for each to come up before starting in order to account for the
+ possibility that a given node might have been rebooted or crashed
+ beforehand.
+ """
+ def is_applicable(self):
+ """Return whether this scenario is applicable."""
return True
def setup(self):
- """ Set up the component, returning True on success """
-
+ """Set up the component, returning True on success."""
self._cm.prepare()
# Clear out the cobwebs ;-)
@@ -405,18 +411,14 @@ class BootCluster(ScenarioComponent):
return self._cm.startall(verbose=True, quick=True)
def teardown(self):
- """ Tear down the component """
-
+ """Tear down the component."""
self._cm.log("Stopping Cluster Manager on all nodes")
self._cm.stopall(verbose=True, force=False)
class LeaveBooted(BootCluster):
- """ The LeaveBooted component leaves all nodes up when the scenario
- is complete.
- """
+ """Leave all nodes up when the scenario is complete."""
def teardown(self):
- """ Tear down the component """
-
+ """Tear down the component."""
self._cm.log("Leaving Cluster running on all nodes")
diff --git a/python/pacemaker/_cts/test.py b/python/pacemaker/_cts/test.py
index 577ebb3..d67abf7 100644
--- a/python/pacemaker/_cts/test.py
+++ b/python/pacemaker/_cts/test.py
@@ -1,10 +1,13 @@
-""" A module providing base classes for defining regression tests and groups of
- regression tests. Everything exported here should be considered an abstract
- class that needs to be subclassed in order to do anything useful. Various
- functions will raise NotImplementedError if not overridden by a subclass.
"""
+A module providing base classes.
-__copyright__ = "Copyright 2009-2023 the Pacemaker project contributors"
+These classes are used for defining regression tests and groups of regression
+tests. Everything exported here should be considered an abstract class that
+needs to be subclassed in order to do anything useful. Various functions
+will raise NotImplementedError if not overridden by a subclass.
+"""
+
+__copyright__ = "Copyright 2009-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+)"
__all__ = ["Test", "Tests"]
@@ -23,11 +26,13 @@ from pacemaker._cts.process import pipe_communicate
from pacemaker.buildoptions import BuildOptions
from pacemaker.exitstatus import ExitStatus
+
def find_validator(rng_file):
- """ Return the command line used to validate XML output, or None if the validator
- is not installed.
"""
+ Return the command line used to validate XML output.
+ If no validator is found, return None.
+ """
if os.access("/usr/bin/xmllint", os.X_OK):
if rng_file is None:
return ["xmllint", "-"]
@@ -38,8 +43,7 @@ def find_validator(rng_file):
def rng_directory():
- """ Which directory contains the RNG schema files? """
-
+ """Return the directory containing RNG schema files."""
if "PCMK_schema_directory" in os.environ:
return os.environ["PCMK_schema_directory"]
@@ -50,18 +54,17 @@ def rng_directory():
class Pattern:
- """ A class for checking log files for a given pattern """
+ """A class for checking log files for a given pattern."""
def __init__(self, pat, negative=False, regex=False):
- """ Create a new Pattern instance
-
- Arguments:
-
- pat -- The string to search for
- negative -- If True, pat must not be found in any input
- regex -- If True, pat is a regex and not a substring
"""
+ Create a new Pattern instance.
+ Arguments:
+ pat -- The string to search for
+ negative -- If True, pat must not be found in any input
+ regex -- If True, pat is a regex and not a substring
+ """
self._pat = pat
self.negative = negative
self.regex = regex
@@ -70,8 +73,7 @@ class Pattern:
return self._pat
def match(self, line):
- """ Is this pattern found in the given line? """
-
+ """Return True if this pattern is found in the given line."""
if self.regex:
return re.search(self._pat, line) is not None
@@ -79,33 +81,36 @@ class Pattern:
class Test:
- """ The base class for a single regression test. A single regression test
- may still run multiple commands as part of its execution.
+ """
+ The base class for a single regression test.
+
+ A single regression test may still run multiple commands as part of its
+ execution.
"""
def __init__(self, name, description, **kwargs):
- """ Create a new Test instance. This method must be provided by all
- subclasses, which must call Test.__init__ first.
-
- Arguments:
-
- description -- A user-readable description of the test, helpful in
- identifying what test is running or has failed.
- name -- The name of the test. Command line tools use this
- attribute to allow running only tests with the exact
- name, or tests whose name matches a given pattern.
- This should be unique among all tests.
-
- Keyword arguments:
-
- force_wait --
- logdir -- The base directory under which to create a directory
- to store output and temporary data.
- timeout -- How long to wait for the test to complete.
- verbose -- Whether to print additional information, including
- verbose command output and daemon log files.
"""
-
+ Create a new Test instance.
+
+ This method must be provided by all subclasses, which must call
+ Test.__init__ first.
+
+ Arguments:
+ description -- A user-readable description of the test, helpful in
+ identifying what test is running or has failed.
+ name -- The name of the test. Command line tools use this
+ attribute to allow running only tests with the exact
+ name, or tests whose name matches a given pattern.
+ This should be unique among all tests.
+
+ Keyword arguments:
+ force_wait --
+ logdir -- The base directory under which to create a directory
+ to store output and temporary data.
+ timeout -- How long to wait for the test to complete.
+ verbose -- Whether to print additional information, including
+ verbose command output and daemon log files.
+ """
self.description = description
self.executed = False
self.name = name
@@ -125,15 +130,17 @@ class Test:
self._result_exitcode = ExitStatus.OK
self._result_txt = ""
- ###
- ### PROPERTIES
- ###
+ #
+ # PROPERTIES
+ #
@property
def exitcode(self):
- """ The final exitcode of the Test. If all commands pass, this property
- will be ExitStatus.OK. Otherwise, this property will be the exitcode
- of the first command to fail.
+ """
+ Return the final exitcode of the Test.
+
+ If all commands pass, this property will be ExitStatus.OK. Otherwise,
+ this property will be the exitcode of the first command to fail.
"""
return self._result_exitcode
@@ -143,24 +150,28 @@ class Test:
@property
def logpath(self):
- """ The path to the log for whatever daemon is being tested. Note that
- this requires all subclasses to set self._daemon_location before
- accessing this property or an exception will be raised.
+ """
+ Return the path to the log for whatever daemon is being tested.
+
+ Note that this requires all subclasses to set self._daemon_location
+ before accessing this property or an exception will be raised.
"""
return os.path.join(self.logdir, "%s.log" % self._daemon_location)
- ###
- ### PRIVATE METHODS
- ###
+ #
+ # PRIVATE METHODS
+ #
def _kill_daemons(self):
- """ Kill any running daemons in preparation for executing the test """
+ """Kill any running daemons in preparation for executing the test."""
raise NotImplementedError("_kill_daemons not provided by subclass")
def _match_log_patterns(self):
- """ Check test output for expected patterns, setting self.exitcode and
- self._result_txt as appropriate. Not all subclass will need to do
- this.
+ """
+ Check test output for expected patterns.
+
+ Set self.exitcode and self._result_txt as appropriate. Not all subclass
+ will need to do this.
"""
if len(self._patterns) == 0:
return
@@ -195,41 +206,38 @@ class Test:
self._result_txt = msg % (self.name, n_failed_matches, len(self._patterns), n_negative_matches)
self.exitcode = ExitStatus.ERROR
-
def _new_cmd(self, cmd, args, exitcode, **kwargs):
- """ Add a command to be executed as part of this test.
-
- Arguments:
-
- cmd -- The program to run.
- args -- Commands line arguments to pass to cmd, as a string.
- exitcode -- The expected exit code of cmd. This can be used to
- run a command that is expected to fail.
-
- Keyword arguments:
-
- stdout_match -- If not None, a string that is expected to be
- present in the stdout of cmd. This can be a
- regular expression.
- no_wait -- Do not wait for cmd to complete.
- stdout_negative_match -- If not None, a string that is expected to be
- missing in the stdout of cmd. This can be a
- regualr expression.
- kill -- A command to be run after cmd, typically in
- order to kill a failed process. This should be
- the entire command line including arguments as
- a single string.
- validate -- If True, the output of cmd will be passed to
- xmllint for validation. If validation fails,
- XmlValidationError will be raised.
- check_rng -- If True and validate is True, command output
- will additionally be checked against the
- api-result.rng file.
- check_stderr -- If True, the stderr of cmd will be included in
- output.
- env -- If not None, variables to set in the environment
"""
-
+ Add a command to be executed as part of this test.
+
+ Arguments:
+ cmd -- The program to run.
+ args -- Commands line arguments to pass to cmd, as a string.
+ exitcode -- The expected exit code of cmd. This can be used to
+ run a command that is expected to fail.
+
+ Keyword arguments:
+ stdout_match -- If not None, a string that is expected to be
+ present in the stdout of cmd. This can be a
+ regular expression.
+ no_wait -- Do not wait for cmd to complete.
+ stdout_negative_match -- If not None, a string that is expected to be
+ missing in the stdout of cmd. This can be a
+ regualr expression.
+ kill -- A command to be run after cmd, typically in
+ order to kill a failed process. This should be
+ the entire command line including arguments as
+ a single string.
+ validate -- If True, the output of cmd will be passed to
+ xmllint for validation. If validation fails,
+ XmlValidationError will be raised.
+ check_rng -- If True and validate is True, command output
+ will additionally be checked against the
+ api-result.rng file.
+ check_stderr -- If True, the stderr of cmd will be included in
+ output.
+ env -- If not None, variables to set in the environment
+ """
self._cmds.append(
{
"args": args,
@@ -247,60 +255,52 @@ class Test:
)
def _start_daemons(self):
- """ Start any necessary daemons in preparation for executing the test """
+ """Start any necessary daemons in preparation for executing the test."""
raise NotImplementedError("_start_daemons not provided by subclass")
- ###
- ### PUBLIC METHODS
- ###
+ #
+ # PUBLIC METHODS
+ #
def add_cmd(self, cmd, args, validate=True, check_rng=True, check_stderr=True,
env=None):
- """ Add a simple command to be executed as part of this test """
-
+ """Add a simple command to be executed as part of this test."""
self._new_cmd(cmd, args, ExitStatus.OK, validate=validate, check_rng=check_rng,
check_stderr=check_stderr, env=env)
def add_cmd_and_kill(self, cmd, args, kill_proc):
- """ Add a command and system command to be executed as part of this test """
-
+ """Add a command and system command to be executed as part of this test."""
self._new_cmd(cmd, args, ExitStatus.OK, kill=kill_proc)
def add_cmd_check_stdout(self, cmd, args, match, no_match=None, env=None):
- """ Add a simple command with expected output to be executed as part of this test """
-
+ """Add a simple command with expected output to be executed as part of this test."""
self._new_cmd(cmd, args, ExitStatus.OK, stdout_match=match,
stdout_negative_match=no_match, env=env)
def add_cmd_expected_fail(self, cmd, args, exitcode=ExitStatus.ERROR):
- """ Add a command that is expected to fail to be executed as part of this test """
-
+ """Add a command that is expected to fail to be executed as part of this test."""
self._new_cmd(cmd, args, exitcode)
def add_cmd_no_wait(self, cmd, args):
- """ Add a simple command to be executed (without waiting) as part of this test """
-
+ """Add a simple command to be executed (without waiting) as part of this test."""
self._new_cmd(cmd, args, ExitStatus.OK, no_wait=True)
def add_log_pattern(self, pattern, negative=False, regex=False):
- """ Add a pattern that should appear in the test's logs """
-
+ """Add a pattern that should appear in the test's logs."""
self._patterns.append(Pattern(pattern, negative=negative, regex=regex))
def _signal_dict(self):
- """ Return a dictionary mapping signal numbers to their names """
-
+ """Return a dictionary mapping signal numbers to their names."""
# FIXME: When we support python >= 3.5, this function can be replaced with:
# signal.Signals(self.daemon_process.returncode).name
return {
getattr(signal, _signame): _signame
- for _signame in dir(signal)
- if _signame.startswith("SIG") and not _signame.startswith("SIG_")
+ for _signame in dir(signal)
+ if _signame.startswith("SIG") and not _signame.startswith("SIG_")
}
def clean_environment(self):
- """ Clean up the host after executing a test """
-
+ """Clean up the host after executing a test."""
if self._daemon_process:
if self._daemon_process.poll() is None:
self._daemon_process.terminate()
@@ -330,13 +330,11 @@ class Test:
print("Daemon Output End")
def print_result(self, filler):
- """ Print the result of the last test execution """
-
+ """Print the result of the last test execution."""
print("%s%s" % (filler, self._result_txt))
def run(self):
- """ Execute this test """
-
+ """Execute this test."""
i = 1
self.start_environment()
@@ -384,8 +382,7 @@ class Test:
self.executed = True
def run_cmd(self, args):
- """ Execute a command as part of this test """
-
+ """Execute a command as part of this test."""
cmd = shlex.split(args['args'])
cmd.insert(0, args['cmd'])
@@ -406,10 +403,10 @@ class Test:
if self.verbose:
print("Also running: %s" % args['kill'])
- ### Typically, the kill argument is used to detect some sort of
- ### failure. Without yielding for a few seconds here, the process
- ### launched earlier that is listening for the failure may not have
- ### time to connect to pacemaker-execd.
+ # Typically, the kill argument is used to detect some sort of
+ # failure. Without yielding for a few seconds here, the process
+ # launched earlier that is listening for the failure may not have
+ # time to connect to pacemaker-execd.
time.sleep(2)
subprocess.Popen(shlex.split(args['kill']))
@@ -459,15 +456,13 @@ class Test:
return ExitStatus.OK
def set_error(self, step, cmd):
- """ Record failure of this test """
-
+ """Record failure of this test."""
msg = "FAILURE - '%s' failed at step %d. Command: %s %s"
self._result_txt = msg % (self.name, step, cmd['cmd'], cmd['args'])
self.exitcode = ExitStatus.ERROR
def start_environment(self):
- """ Prepare the host for executing a test """
-
+ """Prepare the host for executing a test."""
if os.path.exists(self.logpath):
os.remove(self.logpath)
@@ -500,32 +495,34 @@ class Test:
if not self.force_wait:
print("\tDaemon %s doesn't seem to have been initialized within %fs."
"\n\tConsider specifying a longer '--timeout' value."
- %(self._daemon_location, self.timeout))
+ % (self._daemon_location, self.timeout))
return
if self.verbose and (now - update_time) >= 5:
print("Waiting for %s to be initialized: %fs ..."
- %(self._daemon_location, now - init_time))
+ % (self._daemon_location, now - init_time))
update_time = now
class Tests:
- """ The base class for a collection of regression tests """
+ """The base class for a collection of regression tests."""
def __init__(self, **kwargs):
- """ Create a new Tests instance. This method must be provided by all
- subclasses, which must call Tests.__init__ first.
+ """
+ Create a new Tests instance.
- Keywork arguments:
+ This method must be provided by all subclasses, which must call
+ Tests.__init__ first.
- force_wait --
- logdir -- The base directory under which to create a directory
- to store output and temporary data.
- timeout -- How long to wait for the test to complete.
- verbose -- Whether to print additional information, including
- verbose command output and daemon log files.
- """
+ Keywork arguments:
+ force_wait --
+ logdir -- The base directory under which to create a directory
+ to store output and temporary data.
+ timeout -- How long to wait for the test to complete.
+ verbose -- Whether to print additional information, including
+ verbose command output and daemon log files.
+ """
self.force_wait = kwargs.get("force_wait", False)
self.logdir = kwargs.get("logdir", "/tmp")
self.timeout = kwargs.get("timeout", 2)
@@ -534,8 +531,7 @@ class Tests:
self._tests = []
def exit(self):
- """ Exit (with error status code if any test failed) """
-
+ """Exit (with error status code if any test failed)."""
for test in self._tests:
if not test.executed:
continue
@@ -546,8 +542,7 @@ class Tests:
sys.exit(ExitStatus.OK)
def print_list(self):
- """ List all registered tests """
-
+ """List all registered tests."""
print("\n==== %d TESTS FOUND ====" % len(self._tests))
print("%35s - %s" % ("TEST NAME", "TEST DESCRIPTION"))
print("%35s - %s" % ("--------------------", "--------------------"))
@@ -558,8 +553,7 @@ class Tests:
print("==== END OF LIST ====\n")
def print_results(self):
- """ Print summary of results of executed tests """
-
+ """Print summary of results of executed tests."""
failures = 0
success = 0
@@ -582,22 +576,19 @@ class Tests:
print("\n--- TOTALS\n Pass:%d\n Fail:%d\n" % (success, failures))
def run_single(self, name):
- """ Run a single named test """
-
+ """Run a single named test."""
for test in self._tests:
if test.name == name:
test.run()
break
def run_tests(self):
- """ Run all tests """
-
+ """Run all tests."""
for test in self._tests:
test.run()
def run_tests_matching(self, pattern):
- """ Run all tests whose name matches a pattern """
-
+ """Run all tests whose name matches a pattern."""
for test in self._tests:
if test.name.count(pattern) != 0:
test.run()
diff --git a/python/pacemaker/_cts/tests/__init__.py b/python/pacemaker/_cts/tests/__init__.py
index 63b34aa..27bef12 100644
--- a/python/pacemaker/_cts/tests/__init__.py
+++ b/python/pacemaker/_cts/tests/__init__.py
@@ -1,8 +1,6 @@
-"""
-Test classes for the `pacemaker._cts` package.
-"""
+"""Test classes for the `pacemaker._cts` package."""
-__copyright__ = "Copyright 2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2023-2024 the Pacemaker project contributors"
__license__ = "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)"
from pacemaker._cts.tests.componentfail import ComponentFail
@@ -33,12 +31,14 @@ from pacemaker._cts.tests.stonithdtest import StonithdTest
from pacemaker._cts.tests.stoponebyone import StopOnebyOne
from pacemaker._cts.tests.stoptest import StopTest
+
def test_list(cm, audits):
- """ Return a list of test class objects that are enabled and whose
- is_applicable methods return True. These are the tests that
- should be run.
"""
+ Return a list of runnable test class objects.
+ These are objects that are enabled and whose is_applicable methods return
+ True.
+ """
# cm is a reasonable name here.
# pylint: disable=invalid-name
diff --git a/python/pacemaker/_cts/tests/componentfail.py b/python/pacemaker/_cts/tests/componentfail.py
index f3d3622..0832407 100644
--- a/python/pacemaker/_cts/tests/componentfail.py
+++ b/python/pacemaker/_cts/tests/componentfail.py
@@ -1,7 +1,7 @@
-""" Kill a pacemaker daemon and test how the cluster recovers """
+"""Kill a pacemaker daemon and test how the cluster recovers."""
__all__ = ["ComponentFail"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -22,18 +22,15 @@ from pacemaker._cts.tests.simulstartlite import SimulStartLite
class ComponentFail(CTSTest):
- """ A concrete test that kills a random pacemaker daemon and waits for the
- cluster to recover
- """
+ """Kill a random pacemaker daemon and wait for the cluster to recover."""
def __init__(self, cm):
- """ Create a new ComponentFail instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new ComponentFail instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.is_unsafe = True
@@ -45,8 +42,7 @@ class ComponentFail(CTSTest):
self._startall = SimulStartLite(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
self._patterns = []
self._okerrpatterns = []
@@ -159,8 +155,7 @@ class ComponentFail(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
# Note that okerrpatterns refers to the last time we ran this test
# The good news is that this works fine for us...
self._okerrpatterns.extend(self._patterns)
diff --git a/python/pacemaker/_cts/tests/ctstest.py b/python/pacemaker/_cts/tests/ctstest.py
index 8669e48..3ed4931 100644
--- a/python/pacemaker/_cts/tests/ctstest.py
+++ b/python/pacemaker/_cts/tests/ctstest.py
@@ -1,7 +1,7 @@
-""" Base classes for CTS tests """
+"""Base classes for CTS tests."""
__all__ = ["CTSTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -18,25 +18,23 @@ from pacemaker._cts.watcher import LogWatcher
# possibility that we'll miss some other cause of the same warning, but we'll
# just have to be careful.
-# pylint doesn't understand that self._rsh is callable.
-# pylint: disable=not-callable
-
class CTSTest:
- """ The base class for all cluster tests. This implements a basic set of
- properties and behaviors like setup, tear down, time keeping, and
- statistics tracking. It is up to specific tests to implement their own
- specialized behavior on top of this class.
"""
+ The base class for all cluster tests.
- def __init__(self, cm):
- """ Create a new CTSTest instance
-
- Arguments:
+ This implements a basic set of properties and behaviors like setup, tear
+ down, time keeping, and statistics tracking. It is up to specific tests
+ to implement their own specialized behavior on top of this class.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new CTSTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
# pylint: disable=invalid-name
self.audits = []
@@ -68,28 +66,22 @@ class CTSTest:
self.passed = True
def log(self, args):
- """ Log a message """
-
+ """Log a message."""
self._logger.log(args)
def debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
self._logger.debug(args)
def get_timer(self, key="test"):
- """ Get the start time of the given timer """
-
+ """Get the start time of the given timer."""
try:
return self._timers[key].start_time
except KeyError:
return 0
def set_timer(self, key="test"):
- """ Set the start time of the given timer to now, and return
- that time
- """
-
+ """Set the start time of the given timer to now, and return that time."""
if key not in self._timers:
self._timers[key] = Timer(self._logger, self.name, key)
@@ -97,8 +89,7 @@ class CTSTest:
return self._timers[key].start_time
def log_timer(self, key="test"):
- """ Log the elapsed time of the given timer """
-
+ """Log the elapsed time of the given timer."""
if key not in self._timers:
return
@@ -107,8 +98,7 @@ class CTSTest:
del self._timers[key]
def incr(self, name):
- """ Increment the given stats key """
-
+ """Increment the given stats key."""
if name not in self.stats:
self.stats[name] = 0
@@ -119,8 +109,7 @@ class CTSTest:
self.passed = True
def failure(self, reason="none"):
- """ Increment the failure count, with an optional failure reason """
-
+ """Increment the failure count, with an optional failure reason."""
self.passed = False
self.incr("failure")
self._logger.log(("Test %s" % self.name).ljust(35) + " FAILED: %s" % reason)
@@ -128,27 +117,21 @@ class CTSTest:
return False
def success(self):
- """ Increment the success count """
-
+ """Increment the success count."""
self.incr("success")
return True
def skipped(self):
- """ Increment the skipped count """
-
+ """Increment the skipped count."""
self.incr("skipped")
return True
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
raise NotImplementedError
def audit(self):
- """ Perform all the relevant audits (see ClusterAudit), returning
- whether or not they all passed.
- """
-
+ """Perform all the relevant audits (see ClusterAudit), returning whether or not they all passed."""
passed = True
for audit in self.audits:
@@ -160,39 +143,52 @@ class CTSTest:
return passed
def setup(self, node):
- """ Setup this test """
-
+ """Set up this test."""
# node is used in subclasses
# pylint: disable=unused-argument
return self.success()
def teardown(self, node):
- """ Tear down this test """
-
+ """Tear down this test."""
# node is used in subclasses
# pylint: disable=unused-argument
return self.success()
def create_watch(self, patterns, timeout, name=None):
- """ Create a new LogWatcher object with the given patterns, timeout,
- and optional name. This object can be used to search log files
- for matching patterns during this test's run.
+ """
+ Create a new LogWatcher object.
+
+ This object can be used to search log files for matching patterns
+ during this test's run.
+
+ Arguments:
+ patterns -- A list of regular expressions to match against the log
+ timeout -- Default number of seconds to watch a log file at a time;
+ this can be overridden by the timeout= parameter to
+ self.look on an as-needed basis
+ name -- A unique name to use when logging about this watch
"""
if not name:
name = self.name
- return LogWatcher(self._env["LogFileName"], patterns, self._env["nodes"], self._env["LogWatcher"], name, timeout)
+ return LogWatcher(self._env["LogFileName"], patterns,
+ self._env["nodes"], self._env["log_kind"], name,
+ timeout)
def local_badnews(self, prefix, watch, local_ignore=None):
- """ Use the given watch object to search through log files for messages
- starting with the given prefix. If no prefix is given, use
- "LocalBadNews:" by default. The optional local_ignore list should
- be a list of regexes that, if found in a line, will cause that line
- to be ignored.
+ """
+ Search through log files for messages.
- Return the number of matches found.
+ Arguments:
+ prefix -- The string to look for at the beginning of lines,
+ or "LocalBadNews:" if None.
+ watch -- The LogWatcher object to use for searching.
+ local_ignore -- A list of regexes that, if found in a line, will
+ cause that line to be ignored.
+
+ Return the number of matches found.
"""
errcount = 0
if not prefix:
@@ -224,10 +220,11 @@ class CTSTest:
return errcount
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration.
- This method must be implemented by all subclasses.
"""
+ Return True if this test is applicable in the current test configuration.
+ This method must be implemented by all subclasses.
+ """
if self.is_loop and not self._env["loop-tests"]:
return False
@@ -247,6 +244,5 @@ class CTSTest:
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return []
diff --git a/python/pacemaker/_cts/tests/fliptest.py b/python/pacemaker/_cts/tests/fliptest.py
index 5e77936..52692f4 100644
--- a/python/pacemaker/_cts/tests/fliptest.py
+++ b/python/pacemaker/_cts/tests/fliptest.py
@@ -1,7 +1,7 @@
-""" Stop running nodes, and start stopped nodes """
+"""Stop running nodes, and start stopped nodes."""
__all__ = ["FlipTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import time
@@ -20,16 +20,15 @@ from pacemaker._cts.tests.stoptest import StopTest
class FlipTest(CTSTest):
- """ A concrete test that stops running nodes and starts stopped nodes """
+ """Stop running nodes and start stopped nodes."""
def __init__(self, cm):
- """ Create a new FlipTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new FlipTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "Flip"
@@ -37,8 +36,7 @@ class FlipTest(CTSTest):
self._stop = StopTest(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
if self._cm.expected_status[node] == "up":
diff --git a/python/pacemaker/_cts/tests/maintenancemode.py b/python/pacemaker/_cts/tests/maintenancemode.py
index 3c57c07..7ec061a 100644
--- a/python/pacemaker/_cts/tests/maintenancemode.py
+++ b/python/pacemaker/_cts/tests/maintenancemode.py
@@ -1,7 +1,7 @@
-""" Toggle nodes in and out of maintenance mode """
+"""Toggle nodes in and out of maintenance mode."""
__all__ = ["MaintenanceMode"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -22,16 +22,15 @@ from pacemaker._cts.timer import Timer
class MaintenanceMode(CTSTest):
- """ A concrete test that toggles nodes in and out of maintenance mode """
+ """Toggle nodes in and ount of maintenance mode."""
def __init__(self, cm):
- """ Create a new MaintenanceMode instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new MaintenanceMode instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.benchmark = True
@@ -43,8 +42,7 @@ class MaintenanceMode(CTSTest):
self._startall = SimulStartLite(cm)
def _toggle_maintenance_mode(self, node, enabled):
- """ Toggle maintenance mode on the given node """
-
+ """Toggle maintenance mode on the given node."""
pats = [
self.templates["Pat:DC_IDLE"]
]
@@ -83,8 +81,7 @@ class MaintenanceMode(CTSTest):
return ""
def _insert_maintenance_dummy(self, node):
- """ Create a dummy resource on the given node """
-
+ """Create a dummy resource on the given node."""
pats = [
("%s.*" % node) + (self.templates["Pat:RscOpOK"] % ("start", self._rid))
]
@@ -104,8 +101,7 @@ class MaintenanceMode(CTSTest):
return ""
def _remove_maintenance_dummy(self, node):
- """ Remove the previously created dummy resource on the given node """
-
+ """Remove the previously created dummy resource on the given node."""
pats = [
self.templates["Pat:RscOpOK"] % ("stop", self._rid)
]
@@ -124,8 +120,7 @@ class MaintenanceMode(CTSTest):
return ""
def _managed_rscs(self, node):
- """ Return a list of all resources managed by the cluster """
-
+ """Return a list of all resources managed by the cluster."""
rscs = []
(_, lines) = self._rsh(node, "crm_resource -c", verbose=1)
@@ -139,10 +134,7 @@ class MaintenanceMode(CTSTest):
return rscs
def _verify_resources(self, node, rscs, managed):
- """ Verify that all resources in rscList are managed if they are expected
- to be, or unmanaged if they are expected to be.
- """
-
+ """Verify that all resources are managed or unmanaged as expected."""
managed_rscs = rscs
managed_str = "managed"
@@ -171,8 +163,7 @@ class MaintenanceMode(CTSTest):
return False
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
verify_managed = False
verify_unmanaged = False
@@ -227,8 +218,7 @@ class MaintenanceMode(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return [
r"Updating failcount for %s" % self._rid,
r"schedulerd.*: Recover\s+%s\s+\(.*\)" % self._rid,
diff --git a/python/pacemaker/_cts/tests/nearquorumpointtest.py b/python/pacemaker/_cts/tests/nearquorumpointtest.py
index c5b70b7..343cc6e 100644
--- a/python/pacemaker/_cts/tests/nearquorumpointtest.py
+++ b/python/pacemaker/_cts/tests/nearquorumpointtest.py
@@ -1,7 +1,7 @@
-""" Randomly start and stop nodes to bring the cluster close to the quorum point """
+"""Randomly start and stop nodes to bring the cluster close to the quorum point."""
__all__ = ["NearQuorumPointTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,31 +18,27 @@ from pacemaker._cts.tests.ctstest import CTSTest
class NearQuorumPointTest(CTSTest):
- """ A concrete test that randomly starts and stops nodes to bring the
- cluster close to the quorum point
- """
+ """Randomly start and stop nodes to bring the cluster close to the quorum point."""
def __init__(self, cm):
- """ Create a new NearQuorumPointTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new NearQuorumPointTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "NearQuorumPoint"
def __call__(self, dummy):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
startset = []
stopset = []
stonith = self._cm.prepare_fencing_watcher()
- #decide what to do with each node
+ # decide what to do with each node
for node in self._env["nodes"]:
action = self._env.random_gen.choice(["start", "stop"])
@@ -54,7 +50,7 @@ class NearQuorumPointTest(CTSTest):
self.debug("start nodes:%r" % startset)
self.debug("stop nodes:%r" % stopset)
- #add search patterns
+ # add search patterns
watchpats = []
for node in stopset:
if self._cm.expected_status[node] == "up":
@@ -78,7 +74,7 @@ class NearQuorumPointTest(CTSTest):
watch.set_watch()
- #begin actions
+ # begin actions
for node in stopset:
if self._cm.expected_status[node] == "up":
self._cm.stop_cm_async(node)
@@ -87,7 +83,7 @@ class NearQuorumPointTest(CTSTest):
if self._cm.expected_status[node] == "down":
self._cm.start_cm_async(node)
- #get the result
+ # get the result
if watch.look_for_all():
self._cm.cluster_stable()
self._cm.fencing_cleanup("NearQuorumPoint", stonith)
@@ -95,7 +91,7 @@ class NearQuorumPointTest(CTSTest):
self._logger.log("Warn: Patterns not found: %r" % watch.unmatched)
- #get the "bad" nodes
+ # get the "bad" nodes
upnodes = []
for node in stopset:
if self._cm.stat_cm(node):
diff --git a/python/pacemaker/_cts/tests/partialstart.py b/python/pacemaker/_cts/tests/partialstart.py
index 1b074e6..0cee4f3 100644
--- a/python/pacemaker/_cts/tests/partialstart.py
+++ b/python/pacemaker/_cts/tests/partialstart.py
@@ -1,7 +1,7 @@
-""" Start a node and then tell it to stop before it is fully running """
+"""Start a node and then tell it to stop before it is fully running."""
__all__ = ["PartialStart"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -19,16 +19,15 @@ from pacemaker._cts.tests.stoptest import StopTest
class PartialStart(CTSTest):
- """ A concrete test that interrupts a node before it's finished starting up """
+ """Interrupt a node before it's finished starting up."""
def __init__(self, cm):
- """ Create a new PartialStart instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new PartialStart instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "PartialStart"
@@ -38,8 +37,7 @@ class PartialStart(CTSTest):
self._stopall = SimulStopLite(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._stopall(None)
@@ -47,7 +45,7 @@ class PartialStart(CTSTest):
return self.failure("Setup failed")
watchpats = [
- "pacemaker-controld.*Connecting to .* cluster infrastructure"
+ "pacemaker-controld.*Connecting to .* cluster layer"
]
watch = self.create_watch(watchpats, self._env["DeadTime"] + 10)
watch.set_watch()
@@ -66,8 +64,7 @@ class PartialStart(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
# We might do some fencing in the 2-node case if we make it up far enough
return [
r"Executing reboot fencing operation",
diff --git a/python/pacemaker/_cts/tests/reattach.py b/python/pacemaker/_cts/tests/reattach.py
index 4452bc0..ca3b541 100644
--- a/python/pacemaker/_cts/tests/reattach.py
+++ b/python/pacemaker/_cts/tests/reattach.py
@@ -1,7 +1,7 @@
-""" Restart the cluster and verify resources remain running """
+"""Restart the cluster and verify resources remain running."""
__all__ = ["Reattach"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import re
@@ -24,18 +24,15 @@ from pacemaker._cts.tests.starttest import StartTest
class Reattach(CTSTest):
- """ A concrete test that restarts the cluster and verifies that resources
- remain running throughout
- """
+ """Restart the cluster and verify that resources remain running throughout."""
def __init__(self, cm):
- """ Create a new Reattach instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new Reattach instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "Reattach"
@@ -44,26 +41,24 @@ class Reattach(CTSTest):
self._stopall = SimulStopLite(cm)
def _is_managed(self, node):
- """ Are resources managed by the cluster? """
-
+ """Return whether resources are managed by the cluster."""
(_, is_managed) = self._rsh(node, "crm_attribute -t rsc_defaults -n is-managed -q -G -d true", verbose=1)
is_managed = is_managed[0].strip()
return is_managed == "true"
def _set_unmanaged(self, node):
- """ Disable resource management """
-
+ """Disable resource management."""
self.debug("Disable resource management")
self._rsh(node, "crm_attribute -t rsc_defaults -n is-managed -v false")
def _set_managed(self, node):
- """ Enable resource management """
-
+ """Enable resource management."""
self.debug("Re-enable resource management")
self._rsh(node, "crm_attribute -t rsc_defaults -n is-managed -D")
def _disable_incompatible_rscs(self, node):
- """ Disable resources that are incompatible with this test
+ """
+ Disable resources that are incompatible with this test.
Starts and stops of stonith-class resources are implemented internally
by Pacemaker, which means that they must stop when Pacemaker is
@@ -74,7 +69,6 @@ class Reattach(CTSTest):
Set target-role to "Stopped" for any of these resources in the CIB.
"""
-
self.debug("Disable incompatible (stonith/OCFS2) resources")
xml = """'<meta_attributes id="cts-lab-Reattach-meta">
<nvpair id="cts-lab-Reattach-target-role" name="target-role" value="Stopped"/>
@@ -86,26 +80,24 @@ class Reattach(CTSTest):
return self._rsh(node, self._cm.templates['CibAddXml'] % xml)
def _enable_incompatible_rscs(self, node):
- """ Re-enable resources that were incompatible with this test """
-
+ """Re-enable resources that were incompatible with this test."""
self.debug("Re-enable incompatible (stonith/OCFS2) resources")
xml = """<meta_attributes id="cts-lab-Reattach-meta">"""
return self._rsh(node, """cibadmin --delete --xml-text '%s'""" % xml)
def _reprobe(self, node):
- """ Reprobe all resources
+ """
+ Reprobe all resources.
The placement of some resources (such as promotable-1 in the
lab-generated CIB) is affected by constraints using node-attribute-based
rules. An earlier test may have erased the relevant node attribute, so
do a reprobe, which should add the attribute back.
"""
-
return self._rsh(node, """crm_resource --refresh""")
def setup(self, node):
- """ Setup this test """
-
+ """Set up this test."""
if not self._startall(None):
return self.failure("Startall failed")
@@ -123,8 +115,7 @@ class Reattach(CTSTest):
return self.success()
def teardown(self, node):
- """ Tear down this test """
-
+ """Tear down this test."""
# Make sure 'node' is up
start = StartTest(self._cm)
start(node)
@@ -144,8 +135,7 @@ class Reattach(CTSTest):
return self.success()
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
# Conveniently, the scheduler will display this message when disabling
@@ -178,7 +168,7 @@ class Reattach(CTSTest):
self.debug("Bringing the cluster back up")
ret = self._startall(None)
- time.sleep(5) # allow ping to update the CIB
+ time.sleep(5) # allow ping to update the CIB
if not ret:
self._set_managed(node)
return self.failure("Couldn't restart the cluster")
@@ -214,8 +204,7 @@ class Reattach(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return [
r"resource( was|s were) active at shutdown"
]
diff --git a/python/pacemaker/_cts/tests/remotebasic.py b/python/pacemaker/_cts/tests/remotebasic.py
index 2f25aaf..cfe9661 100644
--- a/python/pacemaker/_cts/tests/remotebasic.py
+++ b/python/pacemaker/_cts/tests/remotebasic.py
@@ -1,30 +1,28 @@
-""" Start and stop a remote node """
+"""Start and stop a remote node."""
__all__ = ["RemoteBasic"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.remotedriver import RemoteDriver
class RemoteBasic(RemoteDriver):
- """ A concrete test that starts and stops a remote node """
+ """Start and stop a remote node."""
def __init__(self, cm):
- """ Create a new RemoteBasic instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RemoteBasic instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
RemoteDriver.__init__(self, cm)
self.name = "RemoteBasic"
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
if not self.start_new_test(node):
return self.failure(self.fail_string)
diff --git a/python/pacemaker/_cts/tests/remotedriver.py b/python/pacemaker/_cts/tests/remotedriver.py
index c5b0292..c24fe7f 100644
--- a/python/pacemaker/_cts/tests/remotedriver.py
+++ b/python/pacemaker/_cts/tests/remotedriver.py
@@ -1,7 +1,7 @@
-""" Base classes for CTS tests """
+"""Base classes for CTS tests."""
__all__ = ["RemoteDriver"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import os
@@ -25,21 +25,22 @@ from pacemaker._cts.timer import Timer
class RemoteDriver(CTSTest):
- """ A specialized base class for cluster tests that run on Pacemaker
- Remote nodes. This builds on top of CTSTest to provide methods
- for starting and stopping services and resources, and managing
- remote nodes. This is still just an abstract class -- specific
- tests need to implement their own specialized behavior.
"""
+ A specialized base class for cluster tests that run on Pacemaker Remote nodes.
- def __init__(self, cm):
- """ Create a new RemoteDriver instance
-
- Arguments:
+ This builds on top of CTSTest to provide methods for starting and stopping
+ services and resources, and managing remote nodes. This is still just an
+ abstract class -- specific tests need to implement their own specialized
+ behavior.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new RemoteDriver instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "RemoteDriver"
@@ -54,10 +55,7 @@ class RemoteDriver(CTSTest):
self.reset()
def reset(self):
- """ Reset the state of this test back to what it was before the test
- was run
- """
-
+ """Reset the state of this test back to what it was before the test was run."""
self.failed = False
self.fail_string = ""
@@ -67,8 +65,7 @@ class RemoteDriver(CTSTest):
self._remote_use_reconnect_interval = self._env.random_gen.choice([True, False])
def fail(self, msg):
- """ Mark test as failed """
-
+ """Mark test as failed."""
self.failed = True
# Always log the failure.
@@ -79,11 +76,12 @@ class RemoteDriver(CTSTest):
self.fail_string = msg
def _get_other_node(self, node):
- """ Get the first cluster node out of the environment that is not the
- given node. Typically, this is used to find some node that will
- still be active that we can run cluster commands on.
"""
+ Get the first cluster node out of the environment that is not the given node.
+ Typically, this is used to find some node that will still be active that
+ we can run cluster commands on.
+ """
for othernode in self._env["nodes"]:
if othernode == node:
# we don't want to try and use the cib that we just shutdown.
@@ -93,58 +91,60 @@ class RemoteDriver(CTSTest):
return othernode
def _del_rsc(self, node, rsc):
- """ Delete the given named resource from the cluster. The given `node`
- is the cluster node on which we should *not* run the delete command.
"""
+ Delete the given named resource from the cluster.
+ The given `node` is the cluster node on which we should *not* run the
+ delete command.
+ """
othernode = self._get_other_node(node)
(rc, _) = self._rsh(othernode, "crm_resource -D -r %s -t primitive" % rsc)
if rc != 0:
self.fail("Removal of resource '%s' failed" % rsc)
def _add_rsc(self, node, rsc_xml):
- """ Add a resource given in XML format to the cluster. The given `node`
- is the cluster node on which we should *not* run the add command.
"""
+ Add a resource given in XML format to the cluster.
+ The given `node` is the cluster node on which we should *not* run the
+ add command.
+ """
othernode = self._get_other_node(node)
(rc, _) = self._rsh(othernode, "cibadmin -C -o resources -X '%s'" % rsc_xml)
if rc != 0:
self.fail("resource creation failed")
def _add_primitive_rsc(self, node):
- """ Add a primitive heartbeat resource for the remote node to the
- cluster. The given `node` is the cluster node on which we should
- *not* run the add command.
"""
+ Add a primitive heartbeat resource for the remote node to the cluster.
+ The given `node` is the cluster node on which we should *not* run the
+ add command.
+ """
rsc_xml = """
<primitive class="ocf" id="%(node)s" provider="heartbeat" type="Dummy">
<meta_attributes id="%(node)s-meta_attributes"/>
<operations>
<op id="%(node)s-monitor-interval-20s" interval="20s" name="monitor"/>
</operations>
-</primitive>""" % {
- "node": self._remote_rsc
-}
+</primitive>""" % {"node": self._remote_rsc}
self._add_rsc(node, rsc_xml)
if not self.failed:
self._remote_rsc_added = True
def _add_connection_rsc(self, node):
- """ Add a primitive connection resource for the remote node to the
- cluster. The given `node` is teh cluster node on which we should
- *not* run the add command.
"""
+ Add a primitive connection resource for the remote node to the cluster.
+ The given `node` is the cluster node on which we should *not* run the
+ add command.
+ """
rsc_xml = """
<primitive class="ocf" id="%(node)s" provider="pacemaker" type="remote">
<instance_attributes id="%(node)s-instance_attributes">
<nvpair id="%(node)s-instance_attributes-server" name="server" value="%(server)s"/>
-""" % {
- "node": self._remote_node, "server": node
-}
+""" % {"node": self._remote_node, "server": node}
if self._remote_use_reconnect_interval:
# Set reconnect interval on resource
@@ -159,17 +159,14 @@ class RemoteDriver(CTSTest):
<op id="%(node)s-monitor-20s" name="monitor" interval="20s" timeout="45s"/>
</operations>
</primitive>
-""" % {
- "node": self._remote_node
-}
+""" % {"node": self._remote_node}
self._add_rsc(node, rsc_xml)
if not self.failed:
self._remote_node_added = True
def _disable_services(self, node):
- """ Disable the corosync and pacemaker services on the given node """
-
+ """Disable the corosync and pacemaker services on the given node."""
self._corosync_enabled = self._env.service_is_enabled(node, "corosync")
if self._corosync_enabled:
self._env.disable_service(node, "corosync")
@@ -179,8 +176,7 @@ class RemoteDriver(CTSTest):
self._env.disable_service(node, "pacemaker")
def _enable_services(self, node):
- """ Enable the corosync and pacemaker services on the given node """
-
+ """Enable the corosync and pacemaker services on the given node."""
if self._corosync_enabled:
self._env.enable_service(node, "corosync")
@@ -188,8 +184,7 @@ class RemoteDriver(CTSTest):
self._env.enable_service(node, "pacemaker")
def _stop_pcmk_remote(self, node):
- """ Stop the Pacemaker Remote service on the given node """
-
+ """Stop the Pacemaker Remote service on the given node."""
for _ in range(10):
(rc, _) = self._rsh(node, "service pacemaker_remote stop")
if rc != 0:
@@ -198,8 +193,7 @@ class RemoteDriver(CTSTest):
break
def _start_pcmk_remote(self, node):
- """ Start the Pacemaker Remote service on the given node """
-
+ """Start the Pacemaker Remote service on the given node."""
for _ in range(10):
(rc, _) = self._rsh(node, "service pacemaker_remote start")
if rc != 0:
@@ -209,21 +203,20 @@ class RemoteDriver(CTSTest):
break
def _freeze_pcmk_remote(self, node):
- """ Simulate a Pacemaker Remote daemon failure """
-
+ """Simulate a Pacemaker Remote daemon failure."""
self._rsh(node, "killall -STOP pacemaker-remoted")
def _resume_pcmk_remote(self, node):
- """ Simulate the Pacemaker Remote daemon recovering """
-
+ """Simulate the Pacemaker Remote daemon recovering."""
self._rsh(node, "killall -CONT pacemaker-remoted")
def _start_metal(self, node):
- """ Setup a Pacemaker Remote configuration. Remove any existing
- connection resources or nodes. Start the pacemaker_remote service.
- Create a connection resource.
"""
+ Set up a Pacemaker Remote configuration.
+ Remove any existing connection resources or nodes. Start the
+ pacemaker_remote service. Create a connection resource.
+ """
# Cluster nodes are reused as remote nodes in remote tests. If cluster
# services were enabled at boot, in case the remote node got fenced, the
# cluster node would join instead of the expected remote one. Meanwhile
@@ -266,10 +259,7 @@ class RemoteDriver(CTSTest):
self.fail("Unmatched patterns: %s" % watch.unmatched)
def migrate_connection(self, node):
- """ Move the remote connection resource from the node it's currently
- running on to any other available node
- """
-
+ """Move the remote connection resource to any other available node."""
if self.failed:
return
@@ -294,10 +284,11 @@ class RemoteDriver(CTSTest):
self.fail("Unmatched patterns: %s" % watch.unmatched)
def fail_rsc(self, node):
- """ Cause the dummy resource running on a Pacemaker Remote node to fail
- and verify that the failure is logged correctly
"""
+ Cause the dummy resource running on a Pacemaker Remote node to fail.
+ Verify that the failure is logged correctly.
+ """
if self.failed:
return
@@ -321,11 +312,12 @@ class RemoteDriver(CTSTest):
self.fail("Unmatched patterns during rsc fail: %s" % watch.unmatched)
def fail_connection(self, node):
- """ Cause the remote connection resource to fail and verify that the
- node is fenced and the connection resource is restarted on another
- node.
"""
+ Cause the remote connection resource to fail.
+ Verify that the node is fenced and the connection resource is restarted
+ on another node.
+ """
if self.failed:
return
@@ -378,8 +370,7 @@ class RemoteDriver(CTSTest):
self.fail("Unmatched patterns: %s" % watch.unmatched)
def _add_dummy_rsc(self, node):
- """ Add a dummy resource that runs on the Pacemaker Remote node """
-
+ """Add a dummy resource that runs on the Pacemaker Remote node."""
if self.failed:
return
@@ -409,8 +400,7 @@ class RemoteDriver(CTSTest):
self.fail("Unmatched patterns: %s" % watch.unmatched)
def test_attributes(self, node):
- """ Verify that attributes can be set on the Pacemaker Remote node """
-
+ """Verify that attributes can be set on the Pacemaker Remote node."""
if self.failed:
return
@@ -431,11 +421,12 @@ class RemoteDriver(CTSTest):
self.fail("Failed to delete remote-node attribute")
def cleanup_metal(self, node):
- """ Clean up the Pacemaker Remote node configuration previously created by
- _setup_metal. Stop and remove dummy resources and connection resources.
- Stop the pacemaker_remote service. Remove the remote node itself.
"""
+ Clean up the Pacemaker Remote node configuration previously created by _setup_metal.
+ Stop and remove dummy resources and connection resources. Stop the
+ pacemaker_remote service. Remove the remote node itself.
+ """
self._enable_services(node)
if not self._pcmk_started:
@@ -483,10 +474,11 @@ class RemoteDriver(CTSTest):
self._rsh(self._get_other_node(node), "crm_node --force --remove %s" % self._remote_node)
def _setup_env(self, node):
- """ Setup the environment to allow Pacemaker Remote to function. This
- involves generating a key and copying it to all nodes in the cluster.
"""
+ Set up the environment to allow Pacemaker Remote to function.
+ This involves generating a key and copying it to all nodes in the cluster.
+ """
self._remote_node = "remote-%s" % node
# we are assuming if all nodes have a key, that it is
@@ -511,8 +503,7 @@ class RemoteDriver(CTSTest):
os.unlink(keyfile)
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not CTSTest.is_applicable(self):
return False
@@ -524,10 +515,7 @@ class RemoteDriver(CTSTest):
return True
def start_new_test(self, node):
- """ Prepare a remote test for running by setting up its environment
- and resources
- """
-
+ """Prepare a remote test for running by setting up its environment and resources."""
self.incr("calls")
self.reset()
@@ -541,14 +529,12 @@ class RemoteDriver(CTSTest):
return True
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
raise NotImplementedError
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return list of errors which should be ignored."""
return [
r"""is running on remote.*which isn't allowed""",
r"""Connection terminated""",
diff --git a/python/pacemaker/_cts/tests/remotemigrate.py b/python/pacemaker/_cts/tests/remotemigrate.py
index e22e98f..e65dc70 100644
--- a/python/pacemaker/_cts/tests/remotemigrate.py
+++ b/python/pacemaker/_cts/tests/remotemigrate.py
@@ -1,7 +1,7 @@
-""" Move a connection resource from one node to another """
+"""Move a connection resource from one node to another."""
__all__ = ["RemoteMigrate"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.remotedriver import RemoteDriver
@@ -16,23 +16,21 @@ from pacemaker._cts.tests.remotedriver import RemoteDriver
class RemoteMigrate(RemoteDriver):
- """ A concrete test that moves a connection resource from one node to another """
+ """Move a connection resource from one node to another."""
def __init__(self, cm):
- """ Create a new RemoteMigrate instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RemoteMigrate instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
RemoteDriver.__init__(self, cm)
self.name = "RemoteMigrate"
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
# This code is very similar to __call__ in remotestonithd.py, but I don't think
# it's worth turning into a library function nor making one a subclass of the
# other. I think that's more confusing than leaving the duplication.
@@ -52,8 +50,7 @@ class RemoteMigrate(RemoteDriver):
return self.success()
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not RemoteDriver.is_applicable(self):
return False
diff --git a/python/pacemaker/_cts/tests/remoterscfailure.py b/python/pacemaker/_cts/tests/remoterscfailure.py
index 6f221de..46e6b58 100644
--- a/python/pacemaker/_cts/tests/remoterscfailure.py
+++ b/python/pacemaker/_cts/tests/remoterscfailure.py
@@ -1,7 +1,7 @@
-""" Cause the Pacemaker Remote connection resource to fail """
+"""Cause the Pacemaker Remote connection resource to fail."""
__all__ = ["RemoteRscFailure"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.remotedriver import RemoteDriver
@@ -16,24 +16,20 @@ from pacemaker._cts.tests.remotedriver import RemoteDriver
class RemoteRscFailure(RemoteDriver):
- """ A concrete test that causes the Pacemaker Remote connection resource
- to fail
- """
+ """Cause the Pacemaker Remote connection resource to fail."""
def __init__(self, cm):
- """ Create a new RemoteRscFailure instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RemoteRscFailure instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
RemoteDriver.__init__(self, cm)
self.name = "RemoteRscFailure"
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
if not self.start_new_test(node):
return self.failure(self.fail_string)
@@ -54,16 +50,14 @@ class RemoteRscFailure(RemoteDriver):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return list of errors which should be ignored."""
return [
r"schedulerd.*: Recover\s+remote-rsc\s+\(.*\)",
r"Dummy.*: No process state file found"
] + super().errors_to_ignore
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not RemoteDriver.is_applicable(self):
return False
diff --git a/python/pacemaker/_cts/tests/remotestonithd.py b/python/pacemaker/_cts/tests/remotestonithd.py
index f684992..bb55318 100644
--- a/python/pacemaker/_cts/tests/remotestonithd.py
+++ b/python/pacemaker/_cts/tests/remotestonithd.py
@@ -1,32 +1,28 @@
-""" Fail the connection resource and fence the remote node """
+"""Fail the connection resource and fence the remote node."""
__all__ = ["RemoteStonithd"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.remotedriver import RemoteDriver
class RemoteStonithd(RemoteDriver):
- """ A concrete test that fails the connection resource and fences the
- remote node
- """
+ """Fail the connection resource and fence the remote node."""
def __init__(self, cm):
- """ Create a new RemoteStonithd instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RemoteStonithd instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
RemoteDriver.__init__(self, cm)
self.name = "RemoteStonithd"
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
if not self.start_new_test(node):
return self.failure(self.fail_string)
@@ -41,8 +37,7 @@ class RemoteStonithd(RemoteDriver):
return self.success()
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not RemoteDriver.is_applicable(self):
return False
@@ -50,8 +45,7 @@ class RemoteStonithd(RemoteDriver):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return list of errors which should be ignored."""
return [
r"Lost connection to Pacemaker Remote node",
r"Software caused connection abort",
diff --git a/python/pacemaker/_cts/tests/resourcerecover.py b/python/pacemaker/_cts/tests/resourcerecover.py
index 252eb1f..e4c1336 100644
--- a/python/pacemaker/_cts/tests/resourcerecover.py
+++ b/python/pacemaker/_cts/tests/resourcerecover.py
@@ -1,6 +1,6 @@
-""" Fail a random resource and verify its fail count increases """
+"""Fail a random resource and verify its fail count increases."""
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.audits import AuditResource
@@ -19,16 +19,15 @@ from pacemaker._cts.timer import Timer
class ResourceRecover(CTSTest):
- """ A concrete test that fails a random resource """
+ """Fail a random resource."""
def __init__(self, cm):
- """ Create a new ResourceRecover instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new ResourceRecover instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.benchmark = True
@@ -42,8 +41,7 @@ class ResourceRecover(CTSTest):
self._startall = SimulStartLite(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
if not self._startall(None):
@@ -90,8 +88,7 @@ class ResourceRecover(CTSTest):
return self.success()
def _choose_resource(self, node, resourcelist):
- """ Choose a random resource to target """
-
+ """Choose a random resource to target."""
self._rid = self._env.random_gen.choice(resourcelist)
self._rid_alt = self._rid
(_, lines) = self._rsh(node, "crm_resource -c", verbose=1)
@@ -108,14 +105,13 @@ class ResourceRecover(CTSTest):
return None
def _get_failcount(self, node):
- """ Check the fail count of targeted resource on given node """
-
+ """Check the fail count of targeted resource on given node."""
cmd = "crm_failcount --quiet --query --resource %s --operation %s --interval %d --node %s"
(rc, lines) = self._rsh(node, cmd % (self._rid, self._action, self._interval, node),
verbose=1)
if rc != 0 or len(lines) != 1:
- lines = [l.strip() for l in lines]
+ lines = [line.strip() for line in lines]
self._logger.log("crm_failcount on %s failed (%d): %s" % (node, rc, " // ".join(lines)))
return -1
@@ -128,8 +124,7 @@ class ResourceRecover(CTSTest):
return failcount
def _fail_resource(self, rsc, node, pats):
- """ Fail the targeted resource, and verify as expected """
-
+ """Fail the targeted resource, and verify as expected."""
orig_failcount = self._get_failcount(node)
watch = self.create_watch(pats, 60)
@@ -160,12 +155,12 @@ class ResourceRecover(CTSTest):
return self.failure("%s fail count is %d not %d"
% (self._rid, new_failcount, orig_failcount + 1))
- return 0 # Anything but None is success
+ # Anything but None is success
+ return 0
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return [
r"Updating failcount for %s" % self._rid,
r"schedulerd.*: Recover\s+(%s|%s)\s+\(.*\)" % (self._rid, self._rid_alt),
diff --git a/python/pacemaker/_cts/tests/restartonebyone.py b/python/pacemaker/_cts/tests/restartonebyone.py
index 23b3a68..953a4f0 100644
--- a/python/pacemaker/_cts/tests/restartonebyone.py
+++ b/python/pacemaker/_cts/tests/restartonebyone.py
@@ -1,7 +1,7 @@
-""" Restart all nodes in order """
+"""Restart all nodes in order."""
__all__ = ["RestartOnebyOne"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,16 +18,15 @@ from pacemaker._cts.tests.simulstartlite import SimulStartLite
class RestartOnebyOne(CTSTest):
- """ A concrete test that restarts all nodes in order """
+ """Restart all nodes in order."""
def __init__(self, cm):
- """ Create a new RestartOnebyOne instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RestartOnebyOne instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "RestartOnebyOne"
@@ -36,8 +35,7 @@ class RestartOnebyOne(CTSTest):
self._startall = SimulStartLite(cm)
def __call__(self, dummy):
- """ Perform the test """
-
+ """Perform the test."""
self.incr("calls")
ret = self._startall(None)
diff --git a/python/pacemaker/_cts/tests/restarttest.py b/python/pacemaker/_cts/tests/restarttest.py
index 3b628ce..be8c2ac 100644
--- a/python/pacemaker/_cts/tests/restarttest.py
+++ b/python/pacemaker/_cts/tests/restarttest.py
@@ -1,7 +1,7 @@
-""" Stop and restart a node """
+"""Stop and restart a node."""
__all__ = ["RestartTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -10,16 +10,15 @@ from pacemaker._cts.tests.stoptest import StopTest
class RestartTest(CTSTest):
- """ A concrete test that stops and restarts a node """
+ """Stop and restart a node."""
def __init__(self, cm):
- """ Create a new RestartTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new RestartTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.benchmark = True
self.name = "Restart"
@@ -28,8 +27,7 @@ class RestartTest(CTSTest):
self._stop = StopTest(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
self.incr("node:%s" % node)
diff --git a/python/pacemaker/_cts/tests/resynccib.py b/python/pacemaker/_cts/tests/resynccib.py
index fe634d6..e9fa0e0 100644
--- a/python/pacemaker/_cts/tests/resynccib.py
+++ b/python/pacemaker/_cts/tests/resynccib.py
@@ -1,7 +1,7 @@
-""" Start the cluster without a CIB and verify it gets copied from another node """
+"""Start the cluster without a CIB and verify it gets copied from another node."""
__all__ = ["ResyncCIB"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker import BuildOptions
@@ -20,18 +20,15 @@ from pacemaker._cts.tests.simulstoplite import SimulStopLite
class ResyncCIB(CTSTest):
- """ A concrete test that starts the cluster on one node without a CIB and
- verifies the CIB is copied over when the remaining nodes join
- """
+ """Start the cluster on a node without a CIB and verify the CIB is copied over later."""
def __init__(self, cm):
- """ Create a new ResyncCIB instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new ResyncCIB instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "ResyncCIB"
@@ -41,8 +38,7 @@ class ResyncCIB(CTSTest):
self._stopall = SimulStopLite(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
# Shut down all the nodes...
@@ -64,8 +60,7 @@ class ResyncCIB(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
# Errors that occur as a result of the CIB being wiped
return [
r"error.*: v1 patchset error, patch failed to apply: Application of an update diff failed",
diff --git a/python/pacemaker/_cts/tests/simulstart.py b/python/pacemaker/_cts/tests/simulstart.py
index 88a7f2f..7dcf526 100644
--- a/python/pacemaker/_cts/tests/simulstart.py
+++ b/python/pacemaker/_cts/tests/simulstart.py
@@ -1,7 +1,7 @@
-""" Start all stopped nodes simultaneously """
+"""Start all stopped nodes simultaneously."""
__all__ = ["SimulStart"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -10,16 +10,15 @@ from pacemaker._cts.tests.simulstoplite import SimulStopLite
class SimulStart(CTSTest):
- """ A concrete test that starts all stopped nodes simultaneously """
+ """Start all stopped nodes simultaneously."""
def __init__(self, cm):
- """ Create a new SimulStart instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new SimulStart instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "SimulStart"
@@ -28,8 +27,7 @@ class SimulStart(CTSTest):
self._stopall = SimulStopLite(cm)
def __call__(self, dummy):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._stopall(None)
diff --git a/python/pacemaker/_cts/tests/simulstartlite.py b/python/pacemaker/_cts/tests/simulstartlite.py
index c5c51e1..666b3a1 100644
--- a/python/pacemaker/_cts/tests/simulstartlite.py
+++ b/python/pacemaker/_cts/tests/simulstartlite.py
@@ -1,7 +1,7 @@
-""" Simultaneously start stopped nodes """
+"""Simultaneously start stopped nodes."""
__all__ = ["SimulStartLite"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -16,29 +16,25 @@ from pacemaker._cts.tests.ctstest import CTSTest
class SimulStartLite(CTSTest):
- """ A pseudo-test that is only used to set up conditions before running
- some other test. This class starts any stopped nodes more or less
- simultaneously.
+ """
+ A pseudo-test that sets up conditions before running some other test.
- Other test classes should not use this one as a superclass.
+ This class starts any stopped nodes more or less simultaneously. Other test
+ classes should not use this one as a superclass.
"""
def __init__(self, cm):
- """ Create a new SimulStartLite instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new SimulStartLite instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "SimulStartLite"
def __call__(self, dummy):
- """ Start all stopped nodes more or less simultaneously, returning
- whether this succeeded or not.
- """
-
+ """Return whether starting all stopped nodes more or less simultaneously succeeds."""
self.incr("calls")
self.debug("Setup: %s" % self.name)
@@ -65,7 +61,7 @@ class SimulStartLite(CTSTest):
self.templates["Pat:PacemakerUp"] % node])
# Start all the nodes - at about the same time...
- watch = self.create_watch(watchpats, self._env["DeadTime"]+10)
+ watch = self.create_watch(watchpats, self._env["DeadTime"] + 10)
watch.set_watch()
stonith = self._cm.prepare_fencing_watcher()
@@ -128,6 +124,5 @@ class SimulStartLite(CTSTest):
return self.success()
def is_applicable(self):
- """ SimulStartLite is a setup test and never applicable """
-
+ """Return True if this test is applicable in the current test configuration."""
return False
diff --git a/python/pacemaker/_cts/tests/simulstop.py b/python/pacemaker/_cts/tests/simulstop.py
index 174c533..2ce85e3 100644
--- a/python/pacemaker/_cts/tests/simulstop.py
+++ b/python/pacemaker/_cts/tests/simulstop.py
@@ -1,7 +1,7 @@
-""" Stop all running nodes simultaneously """
+"""Stop all running nodes simultaneously."""
__all__ = ["SimulStop"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -10,16 +10,15 @@ from pacemaker._cts.tests.simulstoplite import SimulStopLite
class SimulStop(CTSTest):
- """ A concrete test that stops all running nodes simultaneously """
+ """Stop all running nodes simultaneously."""
def __init__(self, cm):
- """ Create a new SimulStop instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new SimulStop instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "SimulStop"
@@ -28,8 +27,7 @@ class SimulStop(CTSTest):
self._stopall = SimulStopLite(cm)
def __call__(self, dummy):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._startall(None)
diff --git a/python/pacemaker/_cts/tests/simulstoplite.py b/python/pacemaker/_cts/tests/simulstoplite.py
index d2e687e..69f1b9c 100644
--- a/python/pacemaker/_cts/tests/simulstoplite.py
+++ b/python/pacemaker/_cts/tests/simulstoplite.py
@@ -1,7 +1,7 @@
-""" Simultaneously stop running nodes """
+"""Simultaneously stop running nodes."""
__all__ = ["SimulStopLite"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,30 +18,26 @@ from pacemaker._cts.tests.ctstest import CTSTest
class SimulStopLite(CTSTest):
- """ A pseudo-test that is only used to set up conditions before running
- some other test. This class stops any running nodes more or less
- simultaneously. It can be used both to set up a test or to clean up
- a test.
+ """
+ A pseudo-test that sets up conditions before running some other test.
- Other test classes should not use this one as a superclass.
+ This class stops any running nodes more or less simultaneously. It can be
+ used both to set up a test or to clean up a test. Other test classes
+ should not use this one as a superclass.
"""
def __init__(self, cm):
- """ Create a new SimulStopLite instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new SimulStopLite instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "SimulStopLite"
def __call__(self, dummy):
- """ Stop all running nodes more or less simultaneously, returning
- whether this succeeded or not.
- """
-
+ """Return whether stopping all running nodes more or less simultaneously succeeds."""
self.incr("calls")
self.debug("Setup: %s" % self.name)
@@ -57,7 +53,7 @@ class SimulStopLite(CTSTest):
return self.success()
# Stop all the nodes - at about the same time...
- watch = self.create_watch(watchpats, self._env["DeadTime"]+10)
+ watch = self.create_watch(watchpats, self._env["DeadTime"] + 10)
watch.set_watch()
self.set_timer()
@@ -86,6 +82,5 @@ class SimulStopLite(CTSTest):
return self.failure("Missing log message: %s " % watch.unmatched)
def is_applicable(self):
- """ SimulStopLite is a setup test and never applicable """
-
+ """Return True if this test is applicable in the current test configuration."""
return False
diff --git a/python/pacemaker/_cts/tests/splitbraintest.py b/python/pacemaker/_cts/tests/splitbraintest.py
index 09d5f55..711e6da 100644
--- a/python/pacemaker/_cts/tests/splitbraintest.py
+++ b/python/pacemaker/_cts/tests/splitbraintest.py
@@ -1,7 +1,7 @@
-""" Create a split brain cluster and verify a resource is multiply managed """
+"""Create a split brain cluster and verify a resource is multiply managed."""
__all__ = ["SplitBrainTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import time
@@ -23,19 +23,20 @@ from pacemaker._cts.tests.starttest import StartTest
class SplitBrainTest(CTSTest):
- """ A concrete test that creates a split brain cluster and verifies that
- one node in each partition takes over the resource, resulting in two
- nodes running the same resource.
"""
+ Create a split brain cluster.
- def __init__(self, cm):
- """ Create a new SplitBrainTest instance
-
- Arguments:
+ This test verifies that one node in each partition takes over the
+ resource, resulting in two nodes running the same resource.
+ """
- cm -- A ClusterManager instance
+ def __init__(self, cm):
"""
+ Create a new SplitBrainTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.is_experimental = True
@@ -45,8 +46,7 @@ class SplitBrainTest(CTSTest):
self._startall = SimulStartLite(cm)
def _isolate_partition(self, partition):
- """ Create a new partition containing the given nodes """
-
+ """Create a new partition containing the given nodes."""
other_nodes = self._env["nodes"].copy()
for node in partition:
@@ -67,8 +67,7 @@ class SplitBrainTest(CTSTest):
return
def _heal_partition(self, partition):
- """ Move the given nodes out of their own partition back into the cluster """
-
+ """Move the given nodes out of their own partition back into the cluster."""
other_nodes = self._env["nodes"].copy()
for node in partition:
@@ -87,8 +86,7 @@ class SplitBrainTest(CTSTest):
self._cm.unisolate_node(node, other_nodes)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
self.passed = True
partitions = {}
@@ -197,8 +195,7 @@ class SplitBrainTest(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return [
r"Another DC detected:",
r"(ERROR|error).*: .*Application of an update diff failed",
@@ -207,8 +204,7 @@ class SplitBrainTest(CTSTest):
]
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not CTSTest.is_applicable(self):
return False
diff --git a/python/pacemaker/_cts/tests/standbytest.py b/python/pacemaker/_cts/tests/standbytest.py
index a9ce8ec..a3e1734 100644
--- a/python/pacemaker/_cts/tests/standbytest.py
+++ b/python/pacemaker/_cts/tests/standbytest.py
@@ -1,7 +1,7 @@
-""" Put a node into standby mode and check that resources migrate """
+"""Put a node into standby mode and check that resources migrate."""
__all__ = ["StandbyTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,18 +18,15 @@ from pacemaker._cts.tests.starttest import StartTest
class StandbyTest(CTSTest):
- """ A concrete tests that puts a node into standby and checks that resources
- migrate away from the node
- """
+ """Put a node into standby and check that resources migrate away from it."""
def __init__(self, cm):
- """ Create a new StandbyTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StandbyTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.benchmark = True
@@ -45,8 +42,7 @@ class StandbyTest(CTSTest):
# check resources, resources should have been migrated back (SHOULD THEY?)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._startall(None)
if not ret:
@@ -65,7 +61,7 @@ class StandbyTest(CTSTest):
watchpats = [
r"State transition .* -> S_POLICY_ENGINE",
]
- watch = self.create_watch(watchpats, self._env["DeadTime"]+10)
+ watch = self.create_watch(watchpats, self._env["DeadTime"] + 10)
watch.set_watch()
self.debug("Setting node %s to standby mode" % node)
diff --git a/python/pacemaker/_cts/tests/startonebyone.py b/python/pacemaker/_cts/tests/startonebyone.py
index 6a01097..550dacd 100644
--- a/python/pacemaker/_cts/tests/startonebyone.py
+++ b/python/pacemaker/_cts/tests/startonebyone.py
@@ -1,7 +1,7 @@
-""" Start all stopped nodes serially """
+"""Start all stopped nodes serially."""
__all__ = ["StartOnebyOne"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,16 +18,15 @@ from pacemaker._cts.tests.starttest import StartTest
class StartOnebyOne(CTSTest):
- """ A concrete test that starts all stopped nodes serially """
+ """Start all stopped nodes serially."""
def __init__(self, cm):
- """ Create a new StartOnebyOne instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StartOnebyOne instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "StartOnebyOne"
@@ -35,8 +34,7 @@ class StartOnebyOne(CTSTest):
self._stopall = SimulStopLite(cm)
def __call__(self, dummy):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._stopall(None)
diff --git a/python/pacemaker/_cts/tests/starttest.py b/python/pacemaker/_cts/tests/starttest.py
index 6387511..b8103d4 100644
--- a/python/pacemaker/_cts/tests/starttest.py
+++ b/python/pacemaker/_cts/tests/starttest.py
@@ -1,7 +1,7 @@
-""" Start the cluster manager on a given node """
+"""Start the cluster manager on a given node."""
__all__ = ["StartTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -16,27 +16,25 @@ from pacemaker._cts.tests.ctstest import CTSTest
class StartTest(CTSTest):
- """ A pseudo-test that is only used to set up conditions before running
- some other test. This class starts the cluster manager on a given
- node.
+ """
+ A pseudo-test that sets up conditions before running some other test.
- Other test classes should not use this one as a superclass.
+ This class starts the cluster manager on a given node. Other test classes
+ should not use this one as a superclass.
"""
def __init__(self, cm):
- """ Create a new StartTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StartTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "Start"
def __call__(self, node):
- """ Start the given node, returning whether this succeeded or not """
-
+ """Start the given node, returning whether this succeeded or not."""
self.incr("calls")
if self._cm.upcount() == 0:
diff --git a/python/pacemaker/_cts/tests/stonithdtest.py b/python/pacemaker/_cts/tests/stonithdtest.py
index 0dce291..7c65459 100644
--- a/python/pacemaker/_cts/tests/stonithdtest.py
+++ b/python/pacemaker/_cts/tests/stonithdtest.py
@@ -1,7 +1,7 @@
-""" Fence a running node and wait for it to restart """
+"""Fence a running node and wait for it to restart."""
__all__ = ["StonithdTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker.exitstatus import ExitStatus
@@ -21,16 +21,15 @@ from pacemaker._cts.timer import Timer
class StonithdTest(CTSTest):
- """ A concrete test that fences a running node and waits for it to restart """
+ """Fence a running node and wait for it to restart."""
def __init__(self, cm):
- """ Create a new StonithdTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StonithdTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.benchmark = True
self.name = "Stonithd"
@@ -38,8 +37,7 @@ class StonithdTest(CTSTest):
self._startall = SimulStartLite(cm)
def __call__(self, node):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
if len(self._env["nodes"]) < 2:
return self.skipped()
@@ -122,8 +120,7 @@ class StonithdTest(CTSTest):
@property
def errors_to_ignore(self):
- """ Return list of errors which should be ignored """
-
+ """Return a list of errors which should be ignored."""
return [
self.templates["Pat:Fencing_start"] % ".*",
self.templates["Pat:Fencing_ok"] % ".*",
@@ -132,8 +129,7 @@ class StonithdTest(CTSTest):
]
def is_applicable(self):
- """ Return True if this test is applicable in the current test configuration. """
-
+ """Return True if this test is applicable in the current test configuration."""
if not CTSTest.is_applicable(self):
return False
diff --git a/python/pacemaker/_cts/tests/stoponebyone.py b/python/pacemaker/_cts/tests/stoponebyone.py
index d75d282..4fdfe5c 100644
--- a/python/pacemaker/_cts/tests/stoponebyone.py
+++ b/python/pacemaker/_cts/tests/stoponebyone.py
@@ -1,7 +1,7 @@
-""" Stop all running nodes serially """
+"""Stop all running nodes serially."""
__all__ = ["StopOnebyOne"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,16 +18,15 @@ from pacemaker._cts.tests.stoptest import StopTest
class StopOnebyOne(CTSTest):
- """ A concrete test that stops all running nodes serially """
+ """Stop all running nodes serially."""
def __init__(self, cm):
- """ Create a new StartOnebyOne instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StartOnebyOne instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "StopOnebyOne"
@@ -36,8 +35,7 @@ class StopOnebyOne(CTSTest):
self._stop = StopTest(cm)
def __call__(self, dummy):
- """ Perform this test """
-
+ """Perform this test."""
self.incr("calls")
ret = self._startall(None)
diff --git a/python/pacemaker/_cts/tests/stoptest.py b/python/pacemaker/_cts/tests/stoptest.py
index 8f496d3..48ef73a 100644
--- a/python/pacemaker/_cts/tests/stoptest.py
+++ b/python/pacemaker/_cts/tests/stoptest.py
@@ -1,7 +1,7 @@
-""" Stop the cluster manager on a given node """
+"""Stop the cluster manager on a given node."""
__all__ = ["StopTest"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
from pacemaker._cts.tests.ctstest import CTSTest
@@ -18,27 +18,25 @@ from pacemaker._cts.tests.ctstest import CTSTest
class StopTest(CTSTest):
- """ A pseudo-test that is only used to set up conditions before running
- some other test. This class stops the cluster manager on a given
- node.
+ """
+ A pseudo-test that sets up conditions before running some other test.
- Other test classes should not use this one as a superclass.
+ This class stops the cluster manager on a given node. Other test classes
+ should not use this one as a superclass.
"""
def __init__(self, cm):
- """ Create a new StopTest instance
-
- Arguments:
-
- cm -- A ClusterManager instance
"""
+ Create a new StopTest instance.
+ Arguments:
+ cm -- A ClusterManager instance
+ """
CTSTest.__init__(self, cm)
self.name = "Stop"
def __call__(self, node):
- """ Stop the given node, returning whether this succeeded or not """
-
+ """Stop the given node, returning whether this succeeded or not."""
self.incr("calls")
if self._cm.expected_status[node] != "up":
return self.skipped()
@@ -52,7 +50,7 @@ class StopTest(CTSTest):
# (note that this won't work if we have multiple partitions)
for other in self._env["nodes"]:
if self._cm.expected_status[other] == "up" and other != node:
- patterns.append(self.templates["Pat:They_stopped"] %(other, node))
+ patterns.append(self.templates["Pat:They_stopped"] % (other, node))
watch = self.create_watch(patterns, self._env["DeadTime"])
watch.set_watch()
diff --git a/python/pacemaker/_cts/timer.py b/python/pacemaker/_cts/timer.py
index 122b70b..e31f18b 100644
--- a/python/pacemaker/_cts/timer.py
+++ b/python/pacemaker/_cts/timer.py
@@ -1,63 +1,63 @@
-""" Timer-related utilities for CTS """
+"""Timer-related utilities for CTS."""
__all__ = ["Timer"]
-__copyright__ = "Copyright 2000-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2000-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
import time
+
class Timer:
- """ A class for measuring the runtime of some task. A Timer may be used
- manually or as a context manager, like so:
+ """
+ A class for measuring the runtime of some task.
+
+ A Timer may be used manually or as a context manager, like so:
with Timer(logger, "SomeTest", "SomeTimer"):
...
- A Timer runs from when start() is called until the timer is deleted
- or reset() is called. There is no explicit stop method.
+ A Timer runs from when start() is called until the timer is deleted or
+ reset() is called. There is no explicit stop method.
"""
def __init__(self, logger, test_name, timer_name):
- """ Create a new Timer instance.
-
- Arguments:
-
- logger -- A Logger instance that can be used to record when
- the timer stopped
- test_name -- The name of the test this timer is being run for
- timer_name -- The name of this timer
"""
+ Create a new Timer instance.
+ Arguments:
+ logger -- A Logger instance that can be used to record when
+ the timer stopped
+ test_name -- The name of the test this timer is being run for
+ timer_name -- The name of this timer
+ """
self._logger = logger
self._start_time = None
self._test_name = test_name
self._timer_name = timer_name
def __enter__(self):
+ """When used as a context manager, start the timer."""
self.start()
return self
def __exit__(self, *args):
+ """When used as a context manager, log the elapsed time."""
self._logger.debug("%s:%s runtime: %.2f" % (self._test_name, self._timer_name, self.elapsed))
def reset(self):
- """ Restart the timer """
-
+ """Restart the timer."""
self.start()
def start(self):
- """ Start the timer """
-
+ """Start the timer."""
self._start_time = time.time()
@property
def start_time(self):
- """ When did the timer start? """
-
+ """Return when the timer started."""
return self._start_time
@property
def elapsed(self):
- """ How long has the timer been running for? """
-
+ """Return how long the timer has been running for."""
return time.time() - self._start_time
diff --git a/python/pacemaker/_cts/watcher.py b/python/pacemaker/_cts/watcher.py
index 3e6d702..7870a9f 100644
--- a/python/pacemaker/_cts/watcher.py
+++ b/python/pacemaker/_cts/watcher.py
@@ -1,55 +1,53 @@
-""" Log searching classes for Pacemaker's Cluster Test Suite (CTS) """
+"""Log searching classes for Pacemaker's Cluster Test Suite (CTS)."""
__all__ = ["LogKind", "LogWatcher"]
-__copyright__ = "Copyright 2014-2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2014-2024 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
-from enum import Enum, unique
+from enum import Enum, auto, unique
import re
import time
import threading
+from dateutil.parser import isoparser
+
from pacemaker.buildoptions import BuildOptions
+from pacemaker._cts.errors import OutputNotFoundError
from pacemaker._cts.logging import LogFactory
from pacemaker._cts.remote import RemoteFactory
LOG_WATCHER_BIN = "%s/cts-log-watcher" % BuildOptions.DAEMON_DIR
+
@unique
class LogKind(Enum):
- """ The various kinds of log files that can be watched """
+ """The various kinds of log files that can be watched."""
- ANY = 0
- FILE = 1
- REMOTE_FILE = 2
- JOURNAL = 3
+ LOCAL_FILE = auto() # From a local aggregation file on the exerciser
+ REMOTE_FILE = auto() # From a file on each cluster node
+ JOURNAL = auto() # From the systemd journal on each cluster node
def __str__(self):
- if self.value == 0:
- return "any"
- if self.value == 1:
- return "combined syslog"
- if self.value == 2:
- return "remote"
+ """Return a printable string for a LogKind value."""
+ return self.name.lower().replace('_', ' ')
- return "journal"
class SearchObj:
- """ The base class for various kinds of log watchers. Log-specific watchers
- need to be built on top of this one.
"""
+ The base class for various kinds of log watchers.
- def __init__(self, filename, host=None, name=None):
- """ Create a new SearchObj instance
-
- Arguments:
+ Log-specific watchers need to be built on top of this one.
+ """
- filename -- The log to watch
- host -- The cluster node on which to watch the log
- name -- A unique name to use when logging about this watch
+ def __init__(self, filename, host=None, name=None):
"""
+ Create a new SearchObj instance.
- self.cache = []
+ Arguments:
+ filename -- The log to watch
+ host -- The cluster node on which to watch the log
+ name -- A unique name to use when logging about this watch
+ """
self.filename = filename
self.limit = None
self.logger = LogFactory()
@@ -62,6 +60,12 @@ class SearchObj:
else:
self.host = "localhost"
+ self._cache = []
+ self._delegate = None
+
+ async_task = self.harvest_async()
+ async_task.join()
+
def __str__(self):
if self.host:
return "%s:%s" % (self.host, self.filename)
@@ -69,71 +73,72 @@ class SearchObj:
return self.filename
def log(self, args):
- """ Log a message """
-
+ """Log a message."""
message = "lw: %s: %s" % (self, args)
self.logger.log(message)
def debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
message = "lw: %s: %s" % (self, args)
self.logger.debug(message)
- def harvest(self, delegate=None):
- """ Collect lines from a log, optionally calling delegate when complete """
-
- async_task = self.harvest_async(delegate)
- async_task.join()
-
def harvest_async(self, delegate=None):
- """ Collect lines from a log asynchronously, optionally calling delegate
- when complete. This method must be implemented by all subclasses.
"""
+ Collect lines from a log asynchronously.
+ Optionally, also call delegate when complete. This method must be
+ implemented by all subclasses.
+ """
+ raise NotImplementedError
+
+ def harvest_cached(self):
+ """
+ Return cached logs from before the limit timestamp.
+ """
raise NotImplementedError
def end(self):
- """ Mark that a log is done being watched, resetting internal data structures
- to the beginning of the file. Subsequent watches will therefore start
- from the beginning again.
"""
+ Mark that a log is done being watched.
- self.debug("Unsetting the limit")
+ This function also resets internal data structures to the beginning
+ of the file. Subsequent watches will therefore start from the
+ beginning again.
+ """
+ self.debug("Clearing cache and unsetting limit")
+ self._cache = []
self.limit = None
+
class FileObj(SearchObj):
- """ A specialized SearchObj subclass for watching log files """
+ """A specialized SearchObj subclass for watching log files."""
def __init__(self, filename, host=None, name=None):
- """ Create a new FileObj instance
-
- Arguments:
-
- filename -- The file to watch
- host -- The cluster node on which to watch the file
- name -- A unique name to use when logging about this watch
"""
+ Create a new FileObj instance.
+ Arguments:
+ filename -- The file to watch
+ host -- The cluster node on which to watch the file
+ name -- A unique name to use when logging about this watch
+ """
SearchObj.__init__(self, filename, host, name)
- self._delegate = None
-
- self.harvest()
def async_complete(self, pid, returncode, out, err):
- """ Called when an asynchronous log file read is complete. This function
- saves the output from that read for look()/look_for_all() to process
- and records the current position in the journal. Future reads will
- pick back up from that spot.
+ """
+ Handle completion of an asynchronous log file read.
- Arguments:
+ This function saves the output from that read for look()/look_for_all()
+ to process and records the current position in the journal. Future
+ reads will pick back up from that spot.
- pid -- The ID of the process that did the read
- returncode -- The return code of the process that did the read
- out -- stdout from the file read
- err -- stderr from the file read
+ Arguments:
+ pid -- The ID of the process that did the read
+ returncode -- The return code of the process that did the read
+ out -- stdout from the file read
+ err -- stderr from the file read
"""
-
+ messages = []
for line in out:
match = re.search(r"^CTSwatcher:Last read: (\d+)", line)
@@ -145,20 +150,20 @@ class FileObj(SearchObj):
elif re.search(r"^CTSwatcher:", line):
self.debug("Got control line: %s" % line)
else:
- self.cache.append(line)
+ messages.append(line)
if self._delegate:
- self._delegate.async_complete(pid, returncode, self.cache, err)
+ self._delegate.async_complete(pid, returncode, messages, err)
def harvest_async(self, delegate=None):
- """ Collect lines from the log file on a single host asynchronously,
- optionally calling delegate when complete. This can be called
- repeatedly, reading a chunk each time or until the end of the log
- file is hit.
"""
+ Collect lines from the log file on a single host asynchronously.
+ Optionally, call delegate when complete. This can be called
+ repeatedly, reading a chunk each time or until the end of the
+ log file is hit.
+ """
self._delegate = delegate
- self.cache = []
if self.limit and (self.offset == "EOF" or int(self.offset) > self.limit):
if self._delegate:
@@ -166,24 +171,34 @@ class FileObj(SearchObj):
return None
- return self.rsh.call_async(self.host,
- "%s -t %s -p CTSwatcher: -l 200 -f %s -o %s" % (LOG_WATCHER_BIN, self.name, self.filename, self.offset),
- delegate=self)
+ cmd = ("%s -p CTSwatcher: -l 200 -f %s -o %s"
+ % (LOG_WATCHER_BIN, self.filename, self.offset))
+
+ return self.rsh.call_async(self.host, cmd, delegate=self)
+
+ def harvest_cached(self):
+ """
+ Return cached logs from before the limit timestamp.
+ """
+ # cts-log-watcher script renders caching unnecessary for FileObj.
+ # @TODO Caching might be slightly more efficient, if not too complex.
+ return []
def set_end(self):
- """ Internally record where we expect to find the end of a log file,
- which is just the number of lines in the file. Calls to harvest
- from the log file will not go any farther than what this function
- records.
"""
+ Internally record where we expect to find the end of a log file.
+ Calls to harvest from the log file will not go any farther than
+ what this function records.
+ """
if self.limit:
return
+ cmd = ("%s -p CTSwatcher: -l 2 -f %s -o EOF"
+ % (LOG_WATCHER_BIN, self.filename))
+
# pylint: disable=not-callable
- (_, lines) = self.rsh(self.host,
- "%s -t %s -p CTSwatcher: -l 2 -f %s -o %s" % (LOG_WATCHER_BIN, self.name, self.filename, "EOF"),
- verbose=0)
+ (_, lines) = self.rsh(self.host, cmd, verbose=0)
for line in lines:
match = re.search(r"^CTSwatcher:Last read: (\d+)", line)
@@ -191,138 +206,179 @@ class FileObj(SearchObj):
self.limit = int(match.group(1))
self.debug("Set limit to: %d" % self.limit)
+
class JournalObj(SearchObj):
- """ A specialized SearchObj subclass for watching systemd journals """
+ """A specialized SearchObj subclass for watching systemd journals."""
def __init__(self, host=None, name=None):
- """ Create a new JournalObj instance
+ """
+ Create a new JournalObj instance.
- Arguments:
+ Arguments:
+ host -- The cluster node on which to watch the journal
+ name -- A unique name to use when logging about this watch
+ """
+ SearchObj.__init__(self, name, host, name)
+ self._parser = isoparser()
- host -- The cluster node on which to watch the journal
- name -- A unique name to use when logging about this watch
+ def _msg_after_limit(self, msg):
"""
+ Check whether a message was logged after the limit timestamp.
- SearchObj.__init__(self, name, host, name)
- self._delegate = None
- self._hit_limit = False
+ Arguments:
+ msg -- Message to check
- self.harvest()
+ Returns `True` if `msg` was logged after `self.limit`, or `False`
+ otherwise.
+ """
+ if not self.limit:
+ return False
- def async_complete(self, pid, returncode, out, err):
- """ Called when an asynchronous journal read is complete. This function
- saves the output from that read for look()/look_for_all() to process
- and records the current position in the journal. Future reads will
- pick back up from that spot.
+ match = re.search(r"^\S+", msg)
+ if not match:
+ return False
- Arguments:
+ msg_timestamp = match.group(0)
+ msg_dt = self._parser.isoparse(msg_timestamp)
+ return msg_dt > self.limit
- pid -- The ID of the process that did the journal read
- returncode -- The return code of the process that did the journal read
- out -- stdout from the journal read
- err -- stderr from the journal read
+ def _split_msgs_by_limit(self, msgs):
"""
+ Split a sorted list of messages relative to the limit timestamp.
- found_cursor = False
- for line in out:
- match = re.search(r"^-- cursor: ([^.]+)", line)
+ Arguments:
+ msgs -- List of messages to split
- if match:
- found_cursor = True
- self.offset = match.group(1).strip()
- self.debug("Got %d lines, new cursor: %s" % (len(out), self.offset))
- else:
- self.cache.append(line)
+ Returns a tuple:
+ (list of messages logged on or before limit timestamp,
+ list of messages logged after limit timestamp).
+ """
+ # If last message was logged before limit, all messages were
+ if msgs and self._msg_after_limit(msgs[-1]):
+
+ # Else find index of first message logged after limit
+ for idx, msg in enumerate(msgs):
+ if self._msg_after_limit(msg):
+ self.debug("Got %d lines before passing limit timestamp"
+ % idx)
+ return msgs[:idx], msgs[idx:]
+
+ self.debug("Got %s lines" % len(msgs))
+ return msgs, []
+
+ def async_complete(self, pid, returncode, out, err):
+ """
+ Handle completion of an asynchronous journal read.
+
+ This function saves the output from that read for look()/look_for_all()
+ to process and records the current position in the journal. Future
+ reads will pick back up from that spot.
+
+ Arguments:
+ pid -- The ID of the process that did the journal read
+ returncode -- The return code of the process that did the journal read
+ out -- stdout from the journal read
+ err -- stderr from the journal read
+ """
+ if out:
+ # Cursor should always be last line of journalctl output
+ out, cursor_line = out[:-1], out[-1]
+ match = re.search(r"^-- cursor: ([^.]+)", cursor_line)
+ if not match:
+ raise OutputNotFoundError('Cursor not found at end of output:'
+ + '\n%s' % out)
- if self.limit and not found_cursor:
- self._hit_limit = True
- self.debug("Got %d lines but no cursor: %s" % (len(out), self.offset))
+ self.offset = match.group(1).strip()
+ self.debug("Got new cursor: %s" % self.offset)
- # Get the current cursor
- # pylint: disable=not-callable
- (_, out) = self.rsh(self.host, "journalctl -q -n 0 --show-cursor", verbose=0)
- for line in out:
- match = re.search(r"^-- cursor: ([^.]+)", line)
+ before, after = self._split_msgs_by_limit(out)
- if match:
- self.offset = match.group(1).strip()
- self.debug("Got %d lines, new cursor: %s" % (len(out), self.offset))
- else:
- self.log("Not a new cursor: %s" % line)
- self.cache.append(line)
+ # Save remaining messages after limit for later processing
+ self._cache.extend(after)
if self._delegate:
- self._delegate.async_complete(pid, returncode, self.cache, err)
+ self._delegate.async_complete(pid, returncode, before, err)
def harvest_async(self, delegate=None):
- """ Collect lines from the journal on a single host asynchronously,
- optionally calling delegate when complete. This can be called
- repeatedly, reading a chunk each time or until the end of the
- journal is hit.
"""
+ Collect lines from the journal on a single host asynchronously.
+ Optionally, call delegate when complete. This can be called
+ repeatedly, reading a chunk each time or until the end of the journal
+ is hit.
+ """
self._delegate = delegate
- self.cache = []
-
- # Use --lines to prevent journalctl from overflowing the Popen input buffer
- if self.limit and self._hit_limit:
- return None
+ # Use --lines to prevent journalctl from overflowing the Popen input
+ # buffer
+ command = "journalctl --quiet --output=short-iso --show-cursor"
if self.offset == "EOF":
- command = "journalctl -q -n 0 --show-cursor"
- elif self.limit:
- command = "journalctl -q --after-cursor='%s' --until '%s' --lines=200 --show-cursor" % (self.offset, self.limit)
+ command += " --lines 0"
else:
- command = "journalctl -q --after-cursor='%s' --lines=200 --show-cursor" % (self.offset)
+ command += " --after-cursor='%s' --lines=200" % self.offset
return self.rsh.call_async(self.host, command, delegate=self)
+ def harvest_cached(self):
+ """
+ Return cached logs from before the limit timestamp.
+ """
+ before, self._cache = self._split_msgs_by_limit(self._cache)
+ return before
+
def set_end(self):
- """ Internally record where we expect to find the end of a host's journal,
- which is just the current time. Calls to harvest from the journal will
- not go any farther than what this function records.
"""
+ Internally record where we expect to find the end of a host's journal.
+ Calls to harvest from the journal will not go any farther than what
+ this function records.
+ """
if self.limit:
return
- self._hit_limit = False
+ # --iso-8601=seconds yields YYYY-MM-DDTHH:MM:SSZ, where Z is timezone
+ # as offset from UTC
+
# pylint: disable=not-callable
- (rc, lines) = self.rsh(self.host, "date +'%Y-%m-%d %H:%M:%S'", verbose=0)
+ (rc, lines) = self.rsh(self.host, "date --iso-8601=seconds", verbose=0)
if rc == 0 and len(lines) == 1:
- self.limit = lines[0].strip()
+ self.limit = self._parser.isoparse(lines[0].strip())
self.debug("Set limit to: %s" % self.limit)
else:
self.debug("Unable to set limit for %s because date returned %d lines with status %d"
% (self.host, len(lines), rc))
-class LogWatcher:
- """ A class for watching a single log file or journal across multiple hosts,
- looking for lines that match given regular expressions.
- The way you use this class is as follows:
- - Construct a LogWatcher object
- - Call set_watch() when you want to start watching the log
- - Call look() to scan the log looking for the patterns
+class LogWatcher:
"""
+ Watch a single log file or journal across multiple hosts.
- def __init__(self, log, regexes, hosts, kind=LogKind.ANY, name="Anon", timeout=10, silent=False):
- """ Create a new LogWatcher instance.
+ Instances of this class look for lines that match given regular
+ expressions.
- Arguments:
+ The way you use this class is as follows:
+ - Construct a LogWatcher object
+ - Call set_watch() when you want to start watching the log
+ - Call look() to scan the log looking for the patterns
+ """
- log -- The log file to watch
- regexes -- A list of regular expressions to match against the log
- hosts -- A list of cluster nodes on which to watch the log
- kind -- What type of log is this object watching?
- name -- A unique name to use when logging about this watch
- timeout -- Default number of seconds to watch a log file at a time;
- this can be overridden by the timeout= parameter to
- self.look on an as-needed basis
- silent -- If False, log extra information
+ def __init__(self, log, regexes, hosts, kind, name="Anon", timeout=10,
+ silent=False):
+ """
+ Create a new LogWatcher instance.
+
+ Arguments:
+ log -- The log file to watch
+ regexes -- A list of regular expressions to match against the log
+ hosts -- A list of cluster nodes on which to watch the log
+ kind -- What type of log is this object watching?
+ name -- A unique name to use when logging about this watch
+ timeout -- Default number of seconds to watch a log file at a time;
+ this can be overridden by the timeout= parameter to
+ self.look on an as-needed basis
+ silent -- If False, log extra information
"""
-
self.filename = log
self.hosts = hosts
self.kind = kind
@@ -352,15 +408,16 @@ class LogWatcher:
self._debug("Looking for regex: %s" % regex)
def _debug(self, args):
- """ Log a debug message """
-
+ """Log a debug message."""
message = "lw: %s: %s" % (self.name, args)
self._logger.debug(message)
def set_watch(self):
- """ Mark the place to start watching the log from """
+ """Mark the place to start watching the log from."""
+ if self.kind == LogKind.LOCAL_FILE:
+ self._file_list.append(FileObj(self.filename))
- if self.kind == LogKind.REMOTE_FILE:
+ elif self.kind == LogKind.REMOTE_FILE:
for node in self.hosts:
self._file_list.append(FileObj(self.filename, node, self.name))
@@ -368,26 +425,21 @@ class LogWatcher:
for node in self.hosts:
self._file_list.append(JournalObj(node, self.name))
- else:
- self._file_list.append(FileObj(self.filename))
-
def async_complete(self, pid, returncode, out, err):
- """ Called when an asynchronous log file read is complete. This function
- saves the output from that read for look()/look_for_all() to process
- and records the current position. Future reads will pick back up
- from that spot.
+ """
+ Handle completion of an asynchronous log file read.
- Arguments:
+ This function saves the output from that read for look()/look_for_all()
+ to process and records the current position. Future reads will pick
+ back up from that spot.
- pid -- The ID of the process that did the read
- returncode -- The return code of the process that did the read
- out -- stdout from the file read
- err -- stderr from the file read
+ Arguments:
+ pid -- The ID of the process that did the read
+ returncode -- The return code of the process that did the read
+ out -- stdout from the file read
+ err -- stderr from the file read
"""
-
- # It's not clear to me whether this function ever gets called as
- # delegate somewhere, which is what would pass returncode and err
- # as parameters. Just disable the warning for now.
+ # Called as delegate through {File,Journal}Obj.async_complete()
# pylint: disable=unused-argument
# TODO: Probably need a lock for updating self._line_cache
@@ -398,17 +450,23 @@ class LogWatcher:
self._line_cache.extend(out)
def __get_lines(self):
- """ Iterate over all watched log files and collect new lines from each """
-
+ """Iterate over all watched log files and collect new lines from each."""
if not self._file_list:
raise ValueError("No sources to read from")
pending = []
for f in self._file_list:
- t = f.harvest_async(self)
- if t:
- pending.append(t)
+ cached = f.harvest_cached()
+ if cached:
+ self._debug("Got %d lines from %s cache (total %d)"
+ % (len(cached), f.name, len(self._line_cache)))
+ with self._cache_lock:
+ self._line_cache.extend(cached)
+ else:
+ t = f.harvest_async(self)
+ if t:
+ pending.append(t)
for t in pending:
t.join(60.0)
@@ -417,31 +475,30 @@ class LogWatcher:
return
def end(self):
- """ Mark that a log is done being watched, resetting internal data structures
- to the beginning of the file. Subsequent watches will therefore start
- from the beginning again.
"""
+ Mark that a log is done being watched.
+ This function also resets internal data structures to the beginning
+ of the file. Subsequent watches will therefore start from the
+ beginning again.
+ """
for f in self._file_list:
f.end()
def look(self, timeout=None):
- """ Examine the log looking for the regexes that were given when this
- object was created. It starts looking from the place marked by
- set_watch(), continuing through the file in the fashion of
- `tail -f`. It properly recovers from log file truncation but not
- from removing and recreating the log.
-
- Arguments:
+ """
+ Examine the log looking for the regexes in this object.
- timeout -- Number of seconds to watch the log file; defaults to
- seconds argument passed when this object was created
+ It starts looking from the place marked by set_watch(), continuing
+ through the file in the fashion of `tail -f`. It properly recovers
+ from log file truncation but not from removing and recreating the log.
- Returns:
+ Arguments:
+ timeout -- Number of seconds to watch the log file; defaults to
+ seconds argument passed when this object was created
- The first line which matches any regex
+ Returns the first line which matches any regex
"""
-
if not timeout:
timeout = self._timeout
@@ -495,29 +552,23 @@ class LogWatcher:
self._debug("Waiting: start=%d, end=%d, now=%d, lines=%d" % (begin, end, time.time(), len(self._line_cache)))
time.sleep(1)
- self._debug("How did we get here")
- return None
-
def look_for_all(self, allow_multiple_matches=False, silent=False):
- """ Like look(), but looks for matches for multiple regexes. This function
- returns when the timeout is reached or all regexes were matched. As a
- side effect, self.unmatched will contain regexes that were not matched.
- This can be inspected by the caller.
-
- Arguments:
+ """
+ Like look(), but looks for matches for multiple regexes.
- allow_multiple_matches -- If True, allow each regex to match more than
- once. If False (the default), once a regex
- matches a line, it will no longer be searched
- for.
- silent -- If False, log extra information
+ This function returns when the timeout is reached or all regexes were
+ matched. As a side effect, self.unmatched will contain regexes that
+ were not matched. This can be inspected by the caller.
- Returns:
+ Arguments:
+ allow_multiple_matches -- If True, allow each regex to match more than
+ once. If False (the default), once a regex
+ matches a line, it will no longer be searched
+ for.
+ silent -- If False, log extra information
- If all regexes are matched, return the matching lines. Otherwise, return
- None.
+ Returns the matching lines if all regexes are matched, or None.
"""
-
save_regexes = self.regexes
result = []
diff --git a/python/pacemaker/buildoptions.py.in b/python/pacemaker/buildoptions.py.in
index 17fe981..a97640c 100644
--- a/python/pacemaker/buildoptions.py.in
+++ b/python/pacemaker/buildoptions.py.in
@@ -1,60 +1,65 @@
-""" A module providing information on build-time configuration of pacemaker """
+"""A module providing information on build-time configuration of pacemaker."""
__all__ = ["BuildOptions"]
-__copyright__ = "Copyright 2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2023-2024 the Pacemaker project contributors"
__license__ = "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)"
+
class BuildOptions:
- """ Variables generated as part of the ./configure && make process. These
- affect how pacemaker was configured and where its various parts get
- installed.
+ """
+ Variables generated as part of the ./configure && make process.
+
+ These affect how pacemaker was configured and where its various parts
+ get installed.
"""
BASH_PATH = "@BASH_PATH@"
- """ Path to the bash shell """
+ """Path to the bash shell."""
_BUILD_DIR = "@abs_top_builddir@"
- """ Top-level build directory
- NOTE: This is not especially useful on installed systems, but is useful for
- running various programs from a source checkout
+ """
+ Top-level build directory.
+
+ NOTE: This is not especially useful on installed systems, but is useful for
+ running various programs from a source checkout
"""
CIB_DIR = "@CRM_CONFIG_DIR@"
- """ Where CIB files are stored """
+ """Where CIB files are stored."""
CIB_SCHEMA_VERSION = "@CIB_VERSION@"
- """ Latest supported CIB schema version number """
+ """Latest supported CIB schema version number."""
COROSYNC_CONFIG_FILE = "@PCMK__COROSYNC_CONF@"
- """ Path to the corosync config file """
+ """Path to the corosync config file."""
DAEMON_DIR = "@CRM_DAEMON_DIR@"
- """ Where Pacemaker daemons are installed """
+ """Where Pacemaker daemons are installed."""
DAEMON_USER = "@CRM_DAEMON_USER@"
- """ User to run Pacemaker daemons as """
+ """User to run Pacemaker daemons as."""
LOCAL_STATE_DIR = "@localstatedir@"
- """ Where miscellaneous temporary state files are stored """
+ """Where miscellaneous temporary state files are stored."""
LOG_DIR = "@CRM_LOG_DIR@"
- """ Where Pacemaker log files are stored """
+ """Where Pacemaker log files are stored."""
OCF_RA_INSTALL_DIR = "@OCF_RA_INSTALL_DIR@"
- """ Where resource agents are installed """
+ """Where resource agents are installed."""
OCF_ROOT_DIR = "@OCF_ROOT_DIR@"
- """ Root directory for OCF resource agents and libraries """
+ """Root directory for OCF resource agents and libraries."""
RSC_TMP_DIR = "@CRM_RSCTMP_DIR@"
- """ Where resource agents should keep state files """
+ """Where resource agents should keep state files."""
# pylint: disable=comparison-of-constants
REMOTE_ENABLED = "@PC_NAME_GNUTLS@" != ""
- """ Was Pacemaker Remote support built? """
+ """True if Pacemaker Remote support is enabled."""
SBIN_DIR = "@sbindir@"
- """ Where administrative programs are installed """
+ """Where administrative programs are installed."""
SCHEMA_DIR = "@CRM_SCHEMA_DIRECTORY@"
- """ Where Relax-NG schema files are stored """
+ """Where Relax-NG schema files are stored."""
diff --git a/python/pacemaker/exitstatus.py b/python/pacemaker/exitstatus.py
index f74f9ec..7294d51 100644
--- a/python/pacemaker/exitstatus.py
+++ b/python/pacemaker/exitstatus.py
@@ -1,59 +1,62 @@
-""" A module providing constants relating to why a process or function exited """
+"""A module providing constants relating to why a process or function exited."""
__all__ = ["ExitStatus"]
-__copyright__ = "Copyright 2023 the Pacemaker project contributors"
+__copyright__ = "Copyright 2023-2024 the Pacemaker project contributors"
__license__ = "GNU Lesser General Public License version 2.1 or later (LGPLv2.1+)"
from enum import IntEnum, unique
+
# These values must be kept in sync with include/crm/common/results.h
@unique
class ExitStatus(IntEnum):
- """ Why did a function or process exit? These constants describe both success
- and failure conditions.
+ """
+ Exit status codes for a function or process.
+
+ These constants describe both success and failure conditions.
"""
- OK = 0
- ERROR = 1
- INVALID_PARAM = 2
- UNIMPLEMENT_FEATURE = 3
- INSUFFICIENT_PRIV = 4
- NOT_INSTALLED = 5
- NOT_CONFIGURED = 6
- NOT_RUNNING = 7
- PROMOTED = 8
- FAILED_PROMOTED = 9
- USAGE = 64
- DATAERR = 65
- NOINPUT = 66
- NOUSER = 67
- NOHOST = 68
- UNAVAILABLE = 69
- SOFTWARE = 70
- OSERR = 71
- OSFILE = 72
- CANTCREAT = 73
- IOERR = 74
- TEMPFAIL = 75
- PROTOCOL = 76
- NOPERM = 77
- CONFIG = 78
- FATAL = 100
- PANIC = 101
- DISCONNECT = 102
- OLD = 103
- DIGEST = 104
- NOSUCH = 105
- QUORUM = 106
- UNSAFE = 107
- EXISTS = 108
- MULTIPLE = 109
- EXPIRED = 110
- NOT_YET_IN_EFFECT = 111
- INDETERMINATE = 112
- UNSATISFIED = 113
- TIMEOUT = 124
- DEGRADED = 190
- DEGRADED_PROMOTED = 191
- NONE = 193
- MAX = 255
+ OK = 0
+ ERROR = 1
+ INVALID_PARAM = 2
+ UNIMPLEMENT_FEATURE = 3
+ INSUFFICIENT_PRIV = 4
+ NOT_INSTALLED = 5
+ NOT_CONFIGURED = 6
+ NOT_RUNNING = 7
+ PROMOTED = 8
+ FAILED_PROMOTED = 9
+ USAGE = 64
+ DATAERR = 65
+ NOINPUT = 66
+ NOUSER = 67
+ NOHOST = 68
+ UNAVAILABLE = 69
+ SOFTWARE = 70
+ OSERR = 71
+ OSFILE = 72
+ CANTCREAT = 73
+ IOERR = 74
+ TEMPFAIL = 75
+ PROTOCOL = 76
+ NOPERM = 77
+ CONFIG = 78
+ FATAL = 100
+ PANIC = 101
+ DISCONNECT = 102
+ OLD = 103
+ DIGEST = 104
+ NOSUCH = 105
+ QUORUM = 106
+ UNSAFE = 107
+ EXISTS = 108
+ MULTIPLE = 109
+ EXPIRED = 110
+ NOT_YET_IN_EFFECT = 111
+ INDETERMINATE = 112
+ UNSATISFIED = 113
+ TIMEOUT = 124
+ DEGRADED = 190
+ DEGRADED_PROMOTED = 191
+ NONE = 193
+ MAX = 255
diff --git a/python/tests/test_cts_network.py b/python/tests/test_cts_network.py
index 4aea8b9..3dec000 100644
--- a/python/tests/test_cts_network.py
+++ b/python/tests/test_cts_network.py
@@ -8,6 +8,7 @@ import unittest
from pacemaker._cts.network import next_ip
+
# next_ip makes a bunch of assumptions that we are not going to test here:
#
# * The env argument actually contains an "IPBase" key with a string in it
diff --git a/python/tests/test_exitstatus.py b/python/tests/test_exitstatus.py
index 571f6b4..b8543aa 100644
--- a/python/tests/test_exitstatus.py
+++ b/python/tests/test_exitstatus.py
@@ -8,6 +8,7 @@ import unittest
from pacemaker.exitstatus import ExitStatus
+
class ExitStatusTestCase(unittest.TestCase):
def test_min_max(self):
self.assertEqual(ExitStatus.OK, 0)
diff --git a/rpm/Makefile.am b/rpm/Makefile.am
index 2388ad6..9a454e5 100644
--- a/rpm/Makefile.am
+++ b/rpm/Makefile.am
@@ -162,7 +162,12 @@ $(RPM_SPEC_DIR)/$(PACKAGE).spec: spec-clean pacemaker.spec.in
-e 's/^\(%global commit_abbrev \).*/\1$(SPEC_ABBREV)/' \
-e "s/PACKAGE_DATE/$$(date +'%a %b %d %Y')/" \
-e 's/PACKAGE_VERSION/$(SPEC_RELEASE_NO)-$(SPECVERSION)/' \
- > "$@"
+ > "$@"; \
+ if echo "$$(rpmlint --help 2>&1)" | grep -q "ignore-unused-rpmlintrc"; then \
+ rpmlint --ignore-unused-rpmlintrc --file rpmlintrc "$@"; \
+ else \
+ rpmlint --file rpmlintrc "$@"; \
+ fi
.PHONY: spec $(PACKAGE).spec
spec $(PACKAGE).spec: $(RPM_SPEC_DIR)/$(PACKAGE).spec
@@ -191,10 +196,6 @@ rpm: srpm
rpm-clean: spec-clean srpm-clean
-if [ -n "$(RPM_CLEAN)" ]; then rm -rf $(RPM_CLEAN); fi
-.PHONY: rpmlint
-rpmlint: $(RPM_SPEC_DIR)/$(PACKAGE).spec
- rpmlint -f rpmlintrc "$<"
-
.PHONY: rpm-dep
rpm-dep: $(RPM_SPEC_DIR)/$(PACKAGE).spec
sudo yum-builddep "$(RPM_SPEC_DIR)/$(PACKAGE).spec"
diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in
index c279f88..66ad1da 100644
--- a/rpm/pacemaker.spec.in
+++ b/rpm/pacemaker.spec.in
@@ -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.
#
@@ -204,7 +204,7 @@
## Distro-specific configuration choices
### Use 2.0-style output when other distro packages don't support current output
-%if 0%{?fedora} || ( 0%{?rhel} && 0%{?rhel} <= 8 )
+%if ( 0%{?fedora} && 0%{?fedora} <=35 ) || ( 0%{?rhel} && 0%{?rhel} <= 8 )
%global compat20 --enable-compat-2.0
%endif
@@ -412,6 +412,11 @@ Requires(pre): %{pkgname_shadow_utils}
Requires: %{name}-schemas = %{version}-%{release}
# sbd 1.4.0+ supports the libpe_status API for pe_working_set_t
Conflicts: sbd < 1.4.0
+%if ( 0%{?fedora} && 0%{?fedora} <=35 ) || ( 0%{?rhel} && 0%{?rhel} <= 8 )
+Conflicts: pcs >= 0.11
+%else
+Conflicts: pcs < 0.11
+%endif
%description -n %{pkgname_pcmk_libs}
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -769,6 +774,7 @@ exit 0
%endif
%{_sbindir}/fence_watchdog
+%doc %{_mandir}/man7/pacemaker-based.*
%doc %{_mandir}/man7/pacemaker-controld.*
%doc %{_mandir}/man7/pacemaker-schedulerd.*
%doc %{_mandir}/man7/pacemaker-fenced.*
@@ -849,6 +855,7 @@ exit 0
%{ocf_root}/resource.d/pacemaker
%doc %{_mandir}/man7/*pacemaker*
+%exclude %{_mandir}/man7/pacemaker-based.*
%exclude %{_mandir}/man7/pacemaker-controld.*
%exclude %{_mandir}/man7/pacemaker-schedulerd.*
%exclude %{_mandir}/man7/pacemaker-fenced.*
diff --git a/rpm/rpmlintrc b/rpm/rpmlintrc
index 9104182..2e24773 100644
--- a/rpm/rpmlintrc
+++ b/rpm/rpmlintrc
@@ -6,18 +6,9 @@ addFilter("E: hardcoded-library-path in /usr/lib/os-release")
# When building developer packages
addFilter("W: invalid-url Source0:")
-addFilter("W: unstripped-binary-or-object")
-addFilter("W: incoherent-version-in-changelog")
-addFilter("E: changelog-time-in-future")
# rpmlint doesn't like logrotate script being in pacemaker-cli package
addFilter("E: incoherent-logrotate-file /etc/logrotate.d/pacemaker")
# pacemaker_remote scriptlets use a state file
addFilter("W: dangerous-command-in-%(pre|postun|posttrans) rm")
-
-# We should really use "pacemaker-remote", but we don't
-addFilter("W: incoherent-init-script-name pacemaker_remote")
-
-# Some scriptlets have code for systemd systems only
-addFilter("W: empty-%(post|pre|preun|postun|posttrans)")
diff --git a/tools/attrd_updater.c b/tools/attrd_updater.c
index 5f91356..5316101 100644
--- a/tools/attrd_updater.c
+++ b/tools/attrd_updater.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -18,13 +18,13 @@
#include <sys/types.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/common/ipc_attrd_internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml_internal.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <pcmki/pcmki_output.h>
@@ -85,9 +85,9 @@ private_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError
static gboolean
section_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
- if (pcmk__str_any_of(optarg, "nodes", "forever", NULL)) {
+ if (pcmk__str_any_of(optarg, PCMK_XE_NODES, "forever", NULL)) {
pcmk__set_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
- } else if (pcmk__str_any_of(optarg, "status", "reboot", NULL)) {
+ } else if (pcmk__str_any_of(optarg, PCMK_XE_STATUS, "reboot", NULL)) {
pcmk__clear_node_attr_flags(options.attr_options, pcmk__node_attr_perm);
} else {
g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_USAGE, "Unknown value for --lifetime: %s",
@@ -398,7 +398,7 @@ print_attrd_values(pcmk__output_t *out, const GList *reply)
const pcmk__attrd_query_pair_t *pair = iter->data;
out->message(out, "attribute", NULL, NULL, pair->name, pair->value,
- pair->node);
+ pair->node, false, false);
printed_values = true;
}
}
diff --git a/tools/cibadmin.c b/tools/cibadmin.c
index 44488b5..9ac252d 100644
--- a/tools/cibadmin.c
+++ b/tools/cibadmin.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,7 +10,6 @@
#include <crm_internal.h>
#include <stdio.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc.h>
#include <crm/common/xml.h>
@@ -53,6 +52,7 @@ static struct {
gboolean get_node_path;
gboolean local;
gboolean no_children;
+ gboolean score_update;
gboolean sync_call;
/* @COMPAT: For "-!" version option. Not advertised nor marked as
@@ -79,9 +79,9 @@ print_xml_output(xmlNode * xml)
}
if (pcmk_is_set(options.cmd_options, cib_xpath_address)) {
- const char *id = crm_element_value(xml, XML_ATTR_ID);
+ const char *id = crm_element_value(xml, PCMK_XA_ID);
- if (pcmk__str_eq((const char *)xml->name, "xpath-query", pcmk__str_casei)) {
+ if (pcmk__xe_is(xml, PCMK__XE_XPATH_QUERY)) {
xmlNode *child = NULL;
for (child = xml->children; child; child = child->next) {
@@ -93,9 +93,12 @@ print_xml_output(xmlNode * xml)
}
} else {
- char *buffer = dump_xml_formatted(xml);
- fprintf(stdout, "%s", buffer);
- free(buffer);
+ GString *buf = g_string_sized_new(1024);
+
+ pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buf, 0);
+
+ fprintf(stdout, "%s", buf->str);
+ g_string_free(buf, TRUE);
}
}
@@ -138,18 +141,18 @@ static inline bool
scope_is_valid(const char *scope)
{
return pcmk__str_any_of(scope,
- XML_CIB_TAG_CONFIGURATION,
- XML_CIB_TAG_NODES,
- XML_CIB_TAG_RESOURCES,
- XML_CIB_TAG_CONSTRAINTS,
- XML_CIB_TAG_CRMCONFIG,
- XML_CIB_TAG_RSCCONFIG,
- XML_CIB_TAG_OPCONFIG,
- XML_CIB_TAG_ACLS,
- XML_TAG_FENCING_TOPOLOGY,
- XML_CIB_TAG_TAGS,
- XML_CIB_TAG_ALERTS,
- XML_CIB_TAG_STATUS,
+ PCMK_XE_CONFIGURATION,
+ PCMK_XE_NODES,
+ PCMK_XE_RESOURCES,
+ PCMK_XE_CONSTRAINTS,
+ PCMK_XE_CRM_CONFIG,
+ PCMK_XE_RSC_DEFAULTS,
+ PCMK_XE_OP_DEFAULTS,
+ PCMK_XE_ACLS,
+ PCMK_XE_FENCING_TOPOLOGY,
+ PCMK_XE_TAGS,
+ PCMK_XE_ALERTS,
+ PCMK_XE_STATUS,
NULL);
}
@@ -283,8 +286,8 @@ static GOptionEntry command_entries[] = {
{ "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Delete first object matching supplied criteria (for example, "
- "<" XML_ATTR_OP " " XML_ATTR_ID "=\"rsc1_op1\" "
- XML_ATTR_NAME "=\"monitor\"/>).\n"
+ "<" PCMK_XE_OP " " PCMK_XA_ID "=\"rsc1_op1\" "
+ PCMK_XA_NAME "=\"monitor\"/>).\n"
INDENT "The XML element name and all attributes must match in order for "
"the element to be deleted.",
NULL },
@@ -298,7 +301,7 @@ static GOptionEntry command_entries[] = {
{ "empty", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
command_cb,
"Output an empty CIB. Accepts an optional schema name argument to use as "
- "the " XML_ATTR_VALIDATION " value.\n"
+ "the " PCMK_XA_VALIDATE_WITH " value.\n"
INDENT "If no schema is given, the latest will be used.",
"[schema]" },
@@ -350,12 +353,12 @@ static GOptionEntry addl_entries[] = {
{ "scope", 'o', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, section_cb,
"Limit scope of operation to specific section of CIB\n"
- INDENT "Valid values: " XML_CIB_TAG_CONFIGURATION ", " XML_CIB_TAG_NODES
- ", " XML_CIB_TAG_RESOURCES ", " XML_CIB_TAG_CONSTRAINTS
- ", " XML_CIB_TAG_CRMCONFIG ", " XML_CIB_TAG_RSCCONFIG ",\n"
- INDENT " " XML_CIB_TAG_OPCONFIG ", " XML_CIB_TAG_ACLS
- ", " XML_TAG_FENCING_TOPOLOGY ", " XML_CIB_TAG_TAGS
- ", " XML_CIB_TAG_ALERTS ", " XML_CIB_TAG_STATUS "\n"
+ INDENT "Valid values: " PCMK_XE_CONFIGURATION ", " PCMK_XE_NODES
+ ", " PCMK_XE_RESOURCES ", " PCMK_XE_CONSTRAINTS
+ ", " PCMK_XE_CRM_CONFIG ", " PCMK_XE_RSC_DEFAULTS ",\n"
+ INDENT " " PCMK_XE_OP_DEFAULTS ", " PCMK_XE_ACLS
+ ", " PCMK_XE_FENCING_TOPOLOGY ", " PCMK_XE_TAGS ", " PCMK_XE_ALERTS
+ ", " PCMK_XE_STATUS "\n"
INDENT "If both --scope/-o and --xpath/-a are specified, the last one to "
"appear takes effect",
"value" },
@@ -370,10 +373,10 @@ static GOptionEntry addl_entries[] = {
&options.get_node_path,
"When performing XPath queries, return paths of any matches found\n"
INDENT "(for example, "
- "\"/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
- "/" XML_CIB_TAG_RESOURCES "/" XML_CIB_TAG_INCARNATION
- "[@" XML_ATTR_ID "='dummy-clone']"
- "/" XML_CIB_TAG_RESOURCE "[@" XML_ATTR_ID "='dummy']\")",
+ "\"/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION
+ "/" PCMK_XE_RESOURCES "/" PCMK_XE_CLONE
+ "[@" PCMK_XA_ID "='dummy-clone']"
+ "/" PCMK_XE_PRIMITIVE "[@" PCMK_XA_ID "='dummy']\")",
NULL },
{ "show-access", 'S', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
@@ -386,6 +389,32 @@ static GOptionEntry addl_entries[] = {
INDENT "Default value: 'auto'",
"[value]" },
+ { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update,
+ "Treat new attribute values as atomic score updates where possible "
+ "(with --modify/-M)\n\n"
+
+ INDENT "This currently happens by default and cannot be disabled, but\n"
+ INDENT "this default behavior is deprecated and will be removed in a\n"
+ INDENT "future release. Set this flag if this behavior is desired.\n\n"
+
+ INDENT "This option takes effect when updating XML attributes. For an\n"
+ INDENT "attribute named \"name\", if the new value is \"name++\" or\n"
+ INDENT "\"name+=X\" for some score X, the new value is set as follows:\n"
+ INDENT " * If attribute \"name\" is not already set to some value in\n"
+ INDENT " the element being updated, the new value is set as a literal\n"
+ INDENT " string.\n"
+ INDENT " * If the new value is \"name++\", then the attribute is set to\n"
+ INDENT " its existing value (parsed as a score) plus 1.\n"
+ INDENT " * If the new value is \"name+=X\" for some score X, then the\n"
+ INDENT " attribute is set to its existing value plus X, where the\n"
+ INDENT " existing value and X are parsed and added as scores.\n\n"
+
+ INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n"
+ INDENT "Refer to Pacemaker Explained and to the char2score() function\n"
+ INDENT "for more details on scores, including how they're parsed and\n"
+ INDENT "added.",
+ NULL },
+
{ "allow-create", 'c', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
&options.allow_create,
"(Advanced) Allow target of --modify/-M to be created if it does not "
@@ -430,34 +459,33 @@ build_arg_context(pcmk__common_args_t *args)
"Query the configuration from the local node:\n\n"
"\t# cibadmin --query --local\n\n"
"Query just the cluster options configuration:\n\n"
- "\t# cibadmin --query --scope " XML_CIB_TAG_CRMCONFIG "\n\n"
- "Query all '" XML_RSC_ATTR_TARGET_ROLE "' settings:\n\n"
+ "\t# cibadmin --query --scope " PCMK_XE_CRM_CONFIG "\n\n"
+ "Query all '" PCMK_META_TARGET_ROLE "' settings:\n\n"
"\t# cibadmin --query --xpath "
- "\"//" XML_CIB_TAG_NVPAIR
- "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_TARGET_ROLE"']\""
- "\n\n"
- "Remove all '" XML_RSC_ATTR_MANAGED "' settings:\n\n"
+ "\"//" PCMK_XE_NVPAIR
+ "[@" PCMK_XA_NAME "='" PCMK_META_TARGET_ROLE"']\"\n\n"
+ "Remove all '" PCMK_META_IS_MANAGED "' settings:\n\n"
"\t# cibadmin --delete-all --xpath "
- "\"//" XML_CIB_TAG_NVPAIR
- "[@" XML_NVPAIR_ATTR_NAME "='" XML_RSC_ATTR_MANAGED "']\"\n\n"
+ "\"//" PCMK_XE_NVPAIR
+ "[@" PCMK_XA_NAME "='" PCMK_META_IS_MANAGED "']\"\n\n"
"Remove the resource named 'old':\n\n"
"\t# cibadmin --delete --xml-text "
- "'<" XML_CIB_TAG_RESOURCE " " XML_ATTR_ID "=\"old\"/>'\n\n"
+ "'<" PCMK_XE_PRIMITIVE " " PCMK_XA_ID "=\"old\"/>'\n\n"
"Remove all resources from the configuration:\n\n"
- "\t# cibadmin --replace --scope " XML_CIB_TAG_RESOURCES
- " --xml-text '<" XML_CIB_TAG_RESOURCES "/>'\n\n"
+ "\t# cibadmin --replace --scope " PCMK_XE_RESOURCES
+ " --xml-text '<" PCMK_XE_RESOURCES "/>'\n\n"
"Replace complete configuration with contents of "
"$HOME/pacemaker.xml:\n\n"
"\t# cibadmin --replace --xml-file $HOME/pacemaker.xml\n\n"
- "Replace " XML_CIB_TAG_CONSTRAINTS " section of configuration with "
+ "Replace " PCMK_XE_CONSTRAINTS " section of configuration with "
"contents of $HOME/constraints.xml:\n\n"
- "\t# cibadmin --replace --scope " XML_CIB_TAG_CONSTRAINTS
+ "\t# cibadmin --replace --scope " PCMK_XE_CONSTRAINTS
" --xml-file $HOME/constraints.xml\n\n"
"Increase configuration version to prevent old configurations from "
"being loaded accidentally:\n\n"
"\t# cibadmin --modify --xml-text "
- "'<" XML_TAG_CIB " " XML_ATTR_GENERATION_ADMIN
- "=\"" XML_ATTR_GENERATION_ADMIN "++\"/>'\n\n"
+ "'<" PCMK_XE_CIB " " PCMK_XA_ADMIN_EPOCH
+ "=\"" PCMK_XA_ADMIN_EPOCH "++\"/>'\n\n"
"Edit the configuration with your favorite $EDITOR:\n\n"
"\t# cibadmin --query > $HOME/local.xml\n\n"
"\t# $EDITOR $HOME/local.xml\n\n"
@@ -567,13 +595,14 @@ main(int argc, char **argv)
if (strcmp(options.cib_action, "empty") == 0) {
// Output an empty CIB
- char *buf = NULL;
+ GString *buf = g_string_sized_new(1024);
output = createEmptyCib(1);
- crm_xml_add(output, XML_ATTR_VALIDATION, options.validate_with);
- buf = dump_xml_formatted(output);
- fprintf(stdout, "%s", buf);
- free(buf);
+ crm_xml_add(output, PCMK_XA_VALIDATE_WITH, options.validate_with);
+
+ pcmk__xml_string(output, pcmk__xml_fmt_pretty, buf, 0);
+ fprintf(stdout, "%s", buf->str);
+ g_string_free(buf, TRUE);
goto done;
}
@@ -658,16 +687,16 @@ main(int argc, char **argv)
}
if (options.input_file != NULL) {
- input = filename2xml(options.input_file);
+ input = pcmk__xml_read(options.input_file);
source = options.input_file;
} else if (options.input_xml != NULL) {
- input = string2xml(options.input_xml);
+ input = pcmk__xml_parse(options.input_xml);
source = "input string";
} else if (options.input_stdin) {
+ input = pcmk__xml_read(NULL);
source = "STDIN";
- input = stdin2xml();
} else if (options.acl_render_mode != pcmk__acl_render_none) {
char *username = pcmk__uid2username(geteuid());
@@ -751,12 +780,20 @@ main(int argc, char **argv)
goto done;
}
- version = crm_element_value(input, XML_ATTR_CRM_VERSION);
+ version = crm_element_value(input, PCMK_XA_CRM_FEATURE_SET);
digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version);
fprintf(stderr, "Versioned (%s) digest: ", version);
fprintf(stdout, "%s\n", pcmk__s(digest, "<null>"));
free(digest);
goto done;
+
+ } else if (pcmk__str_eq(options.cib_action, PCMK__CIB_REQUEST_MODIFY,
+ pcmk__str_none)) {
+ /* @COMPAT When we drop default support for expansion in cibadmin, guard
+ * with `if (options.score_update)`
+ */
+ cib__set_call_options(options.cmd_options, crm_system_name,
+ cib_score_update);
}
rc = do_init();
@@ -773,9 +810,13 @@ main(int argc, char **argv)
}
rc = do_work(input, &output);
- if (rc > 0) {
- /* wait for the reply by creating a mainloop and running it until
- * the callbacks are invoked...
+ if (!pcmk_is_set(options.cmd_options, cib_sync_call)
+ && (the_cib->variant != cib_file)
+ && (rc >= 0)) {
+ /* For async call, positive rc is the call ID (file always synchronous).
+ *
+ * Wait for the reply by creating a mainloop and running it until the
+ * callbacks are invoked.
*/
request_id = rc;
@@ -791,32 +832,36 @@ main(int argc, char **argv)
crm_info("Starting mainloop");
g_main_loop_run(mainloop);
- } else if ((rc == -pcmk_err_schema_unchanged)
- && (strcmp(options.cib_action,
- PCMK__CIB_REQUEST_UPGRADE) == 0)) {
- report_schema_unchanged();
-
- } else if (rc < 0) {
+ } else {
rc = pcmk_legacy2rc(rc);
- crm_err("Call failed: %s", pcmk_rc_str(rc));
- fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc));
- if (rc == pcmk_rc_schema_validation) {
- if (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0) {
- xmlNode *obj = NULL;
- int version = 0;
+ if ((rc == pcmk_rc_schema_unchanged)
+ && (strcmp(options.cib_action, PCMK__CIB_REQUEST_UPGRADE) == 0)) {
- if (the_cib->cmds->query(the_cib, NULL, &obj,
- options.cmd_options) == pcmk_ok) {
- update_validation(&obj, &version, 0, TRUE, FALSE);
- }
- free_xml(obj);
+ report_schema_unchanged();
+
+ } else if (rc != pcmk_rc_ok) {
+ crm_err("Call failed: %s", pcmk_rc_str(rc));
+ fprintf(stderr, "Call failed: %s\n", pcmk_rc_str(rc));
+ exit_code = pcmk_rc2exitc(rc);
- } else if (output) {
- validate_xml_verbose(output);
+ if (rc == pcmk_rc_schema_validation) {
+ if (strcmp(options.cib_action,
+ PCMK__CIB_REQUEST_UPGRADE) == 0) {
+ xmlNode *obj = NULL;
+
+ if (the_cib->cmds->query(the_cib, NULL, &obj,
+ options.cmd_options) == pcmk_ok) {
+ pcmk__update_schema(&obj, NULL, true, false);
+ }
+ free_xml(obj);
+
+ } else if (output != NULL) {
+ // Show validation errors to stderr
+ pcmk__validate_xml(output, NULL, NULL, NULL);
+ }
}
}
- exit_code = pcmk_rc2exitc(rc);
}
if ((output != NULL)
@@ -883,11 +928,11 @@ do_work(xmlNode *input, xmlNode **output)
/* construct the request */
the_cib->call_timeout = options.message_timeout_sec;
if ((strcmp(options.cib_action, PCMK__CIB_REQUEST_REPLACE) == 0)
- && pcmk__xe_is(input, XML_TAG_CIB)) {
- xmlNode *status = pcmk_find_cib_element(input, XML_CIB_TAG_STATUS);
+ && pcmk__xe_is(input, PCMK_XE_CIB)) {
+ xmlNode *status = pcmk_find_cib_element(input, PCMK_XE_STATUS);
if (status == NULL) {
- create_xml_node(input, XML_CIB_TAG_STATUS);
+ pcmk__xe_create(input, PCMK_XE_STATUS);
}
}
diff --git a/tools/cibsecret.in b/tools/cibsecret.in
index 4569863..9df4201 100644
--- a/tools/cibsecret.in
+++ b/tools/cibsecret.in
@@ -171,7 +171,7 @@ check_env() {
else
fatal $CRM_EX_NOT_INSTALLED "please install pssh, pdsh, or ssh to run $PROG"
fi
- ps -ef | grep '[p]acemaker-controld' >/dev/null ||
+ ps axww | grep '[p]acemaker-controld' >/dev/null ||
fatal $CRM_EX_UNAVAILABLE "pacemaker not running? $PROG needs pacemaker"
}
diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c
index defe294..f9b1434 100644
--- a/tools/crm_attribute.c
+++ b/tools/crm_attribute.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -22,7 +22,6 @@
#include <sys/types.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ipc.h>
#include <crm/common/util.h>
@@ -30,7 +29,7 @@
#include <crm/cib.h>
#include <crm/cib/internal.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc_attrd_internal.h>
#include <crm/common/ipc_controld.h>
@@ -41,36 +40,18 @@
#define SUMMARY "crm_attribute - query and update Pacemaker cluster options and node attributes"
+enum attr_cmd {
+ attr_cmd_none,
+ attr_cmd_delete,
+ attr_cmd_list,
+ attr_cmd_query,
+ attr_cmd_update,
+};
+
GError *error = NULL;
crm_exit_t exit_code = CRM_EX_OK;
uint64_t cib_opts = cib_sync_call;
-PCMK__OUTPUT_ARGS("attribute", "const char *", "const char *", "const char *",
- "const char *", "const char *")
-static int
-attribute_text(pcmk__output_t *out, va_list args)
-{
- const char *scope = va_arg(args, const char *);
- const char *instance = va_arg(args, const char *);
- const char *name = va_arg(args, const char *);
- const char *value = va_arg(args, const char *);
- const char *host G_GNUC_UNUSED = va_arg(args, const char *);
-
- if (out->quiet) {
- if (value != NULL) {
- pcmk__formatted_printf(out, "%s\n", value);
- }
- } else {
- out->info(out, "%s%s %s%s %s%s value=%s",
- scope ? "scope=" : "", scope ? scope : "",
- instance ? "id=" : "", instance ? instance : "",
- name ? "name=" : "", name ? name : "",
- value ? value : "(null)");
- }
-
- return pcmk_rc_ok;
-}
-
static pcmk__supported_format_t formats[] = {
PCMK__SUPPORTED_FORMAT_NONE,
PCMK__SUPPORTED_FORMAT_TEXT,
@@ -78,14 +59,8 @@ static pcmk__supported_format_t formats[] = {
{ NULL, NULL, NULL }
};
-static pcmk__message_entry_t fmt_functions[] = {
- { "attribute", "text", attribute_text },
-
- { NULL, NULL, NULL }
-};
-
struct {
- char command;
+ enum attr_cmd command;
gchar *attr_default;
gchar *attr_id;
gchar *attr_name;
@@ -98,26 +73,49 @@ struct {
gchar *set_name;
char *set_type;
gchar *type;
- gboolean promotion_score;
+ char *opt_list;
+ gboolean all;
+ bool promotion_score;
+ gboolean score_update;
} options = {
- .command = 'G',
- .promotion_score = FALSE
+ .command = attr_cmd_query,
};
#define INDENT " "
static gboolean
+list_cb(const gchar *option_name, const gchar *optarg, gpointer data,
+ GError **error) {
+ options.command = attr_cmd_list;
+ pcmk__str_update(&options.opt_list, optarg);
+ return TRUE;
+}
+
+static gboolean
delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.command = 'D';
+ options.command = attr_cmd_delete;
pcmk__str_update(&options.attr_value, NULL);
return TRUE;
}
static gboolean
+attr_name_cb(const gchar *option_name, const gchar *optarg, gpointer data,
+ GError **error)
+{
+ options.promotion_score = false;
+
+ if (options.attr_name != NULL) {
+ g_free(options.attr_name);
+ }
+ options.attr_name = g_strdup(optarg);
+ return TRUE;
+}
+
+static gboolean
promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
char *score_name = NULL;
- options.promotion_score = TRUE;
+ options.promotion_score = true;
if (options.attr_name) {
g_free(options.attr_name);
@@ -136,7 +134,7 @@ promotion_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErro
static gboolean
update_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.command = 'u';
+ options.command = attr_cmd_update;
pcmk__str_update(&options.attr_value, optarg);
return TRUE;
}
@@ -147,14 +145,14 @@ utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GEr
g_free(options.type);
}
- options.type = g_strdup(XML_CIB_TAG_NODES);
- pcmk__str_update(&options.set_type, XML_TAG_UTILIZATION);
+ options.type = g_strdup(PCMK_XE_NODES);
+ pcmk__str_update(&options.set_type, PCMK_XE_UTILIZATION);
return TRUE;
}
static gboolean
value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.command = 'G';
+ options.command = attr_cmd_query;
pcmk__str_update(&options.attr_value, NULL);
return TRUE;
}
@@ -180,13 +178,20 @@ wait_cb (const gchar *option_name, const gchar *optarg, gpointer data, GError **
}
static GOptionEntry selecting_entries[] = {
+ { "all", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all,
+ "With -L/--list-options, include advanced and deprecated options in the\n"
+ INDENT "output. This is always treated as true when --output-as=xml is\n"
+ INDENT "specified.",
+ NULL,
+ },
+
{ "id", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
"(Advanced) Operate on instance of specified attribute with this\n"
INDENT "XML ID",
"XML_ID"
},
- { "name", 'n', 0, G_OPTION_ARG_STRING, &options.attr_name,
+ { "name", 'n', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, attr_name_cb,
"Operate on attribute or option with this name. For queries, this\n"
INDENT "is optional, in which case all matching attributes will be\n"
INDENT "returned.",
@@ -217,8 +222,14 @@ static GOptionEntry selecting_entries[] = {
};
static GOptionEntry command_entries[] = {
+ { "list-options", 'L', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, list_cb,
+ "List all available options of the given type.\n"
+ INDENT "Allowed values: " PCMK__VALUE_CLUSTER,
+ "TYPE"
+ },
+
{ "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
- "Delete the attribute/option",
+ "Delete the attribute/option (with -n or -P)",
NULL
},
@@ -229,7 +240,7 @@ static GOptionEntry command_entries[] = {
},
{ "update", 'v', 0, G_OPTION_ARG_CALLBACK, update_cb,
- "Update the value of the attribute/option",
+ "Update the value of the attribute/option (with -n or -P)",
"VALUE"
},
@@ -260,6 +271,35 @@ static GOptionEntry addl_entries[] = {
"SECTION"
},
+ { "score", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.score_update,
+ "Treat new attribute values as atomic score updates where possible\n"
+ INDENT "(with --update/-v, when running against a CIB file or updating\n"
+ INDENT "an attribute outside the " PCMK_XE_STATUS " section; enabled\n"
+ INDENT "by default if --promotion/-p is specified)\n\n"
+
+ INDENT "This currently happens by default and cannot be disabled, but\n"
+ INDENT "this default behavior is deprecated and will be removed in a\n"
+ INDENT "future release (exception: this will remain the default with\n"
+ INDENT "--promotion/-p). Set this flag if this behavior is desired.\n\n"
+
+ INDENT "This option takes effect when updating XML attributes. For an\n"
+ INDENT "attribute named \"name\", if the new value is \"name++\" or\n"
+ INDENT "\"name+=X\" for some score X, the new value is set as follows:\n"
+ INDENT " * If attribute \"name\" is not already set to some value in\n"
+ INDENT " the element being updated, the new value is set as a literal\n"
+ INDENT " string.\n"
+ INDENT " * If the new value is \"name++\", then the attribute is set to\n"
+ INDENT " its existing value (parsed as a score) plus 1.\n"
+ INDENT " * If the new value is \"name+=X\" for some score X, then the\n"
+ INDENT " attribute is set to its existing value plus X, where the\n"
+ INDENT " existing value and X are parsed and added as scores.\n\n"
+
+ INDENT "Scores are integer values capped at INFINITY and -INFINITY.\n"
+ INDENT "Refer to Pacemaker Explained and to the char2score() function\n"
+ INDENT "for more details on scores, including how they're parsed and\n"
+ INDENT "added.",
+ NULL },
+
{ "wait", 'W', 0, G_OPTION_ARG_CALLBACK, wait_cb,
"Wait for some event to occur before returning. Values are 'no' (wait\n"
INDENT "only for the attribute daemon to acknowledge the request),\n"
@@ -288,7 +328,7 @@ static GOptionEntry deprecated_entries[] = {
NULL, NULL
},
- { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &options.attr_name,
+ { "attr-name", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, attr_name_cb,
NULL, NULL
},
@@ -314,36 +354,39 @@ static GOptionEntry deprecated_entries[] = {
static void
get_node_name_from_local(void)
{
- char *hostname = pcmk_hostname();
+ struct utsname hostinfo;
g_free(options.dest_uname);
- /* This silliness is so that dest_uname is always a glib-managed
- * string so we know how to free it later. pcmk_hostname returns
- * a newly allocated string via strdup.
- */
- options.dest_uname = g_strdup(hostname);
- free(hostname);
+ if (uname(&hostinfo) == 0) {
+ options.dest_uname = g_strdup(hostinfo.nodename);
+ } else {
+ options.dest_uname = NULL;
+ }
}
static int
-send_attrd_update(char command, const char *attr_node, const char *attr_name,
- const char *attr_value, const char *attr_set,
- const char *attr_dampen, uint32_t attr_options)
+send_attrd_update(enum attr_cmd command, const char *attr_node,
+ const char *attr_name, const char *attr_value,
+ const char *attr_set, const char *attr_dampen,
+ uint32_t attr_options)
{
int rc = pcmk_rc_ok;
uint32_t opts = attr_options;
switch (command) {
- case 'D':
+ case attr_cmd_delete:
rc = pcmk__attrd_api_delete(NULL, attr_node, attr_name, opts);
break;
- case 'u':
+ case attr_cmd_update:
rc = pcmk__attrd_api_update(NULL, attr_node, attr_name,
attr_value, NULL, attr_set, NULL,
opts | pcmk__node_attr_value);
break;
+
+ default:
+ break;
}
if (rc != pcmk_rc_ok) {
@@ -364,7 +407,7 @@ delete_attr_on_node(xmlNode *child, void *userdata)
{
struct delete_data_s *dd = (struct delete_data_s *) userdata;
- const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
+ const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
int rc = pcmk_rc_ok;
if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
@@ -383,6 +426,22 @@ delete_attr_on_node(xmlNode *child, void *userdata)
return rc;
}
+static void
+command_list(pcmk__output_t *out)
+{
+ if (pcmk__str_eq(options.opt_list, PCMK__VALUE_CLUSTER, pcmk__str_none)) {
+ exit_code = pcmk_rc2exitc(pcmk__list_cluster_options(out, options.all));
+
+ } else {
+ // @TODO Improve usage messages to reduce duplication
+ exit_code = CRM_EX_USAGE;
+ g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
+ "Invalid --list-options value '%s'. Allowed values: "
+ PCMK__VALUE_CLUSTER,
+ pcmk__s(options.opt_list, "(BUG: none)"));
+ }
+}
+
static int
command_delete(pcmk__output_t *out, cib_t *cib)
{
@@ -405,10 +464,6 @@ command_delete(pcmk__output_t *out, cib_t *cib)
rc = pcmk__xe_foreach_child(result, NULL, delete_attr_on_node, &dd);
- if (rc != pcmk_rc_ok) {
- goto done_deleting;
- }
-
} else {
rc = cib__delete_node_attr(out, cib, cib_opts, options.type, options.dest_node,
options.set_type, options.set_name, options.attr_id,
@@ -440,7 +495,7 @@ update_attr_on_node(xmlNode *child, void *userdata)
{
struct update_data_s *ud = (struct update_data_s *) userdata;
- const char *attr_name = crm_element_value(child, XML_NVPAIR_ATTR_NAME);
+ const char *attr_name = crm_element_value(child, PCMK_XA_NAME);
if (!pcmk__str_eq(attr_name, options.attr_pattern, pcmk__str_regex)) {
return pcmk_rc_ok;
@@ -450,7 +505,7 @@ update_attr_on_node(xmlNode *child, void *userdata)
options.dest_node, options.set_type,
options.set_name, options.attr_id,
attr_name, options.attr_value, NULL,
- ud->is_remote_node ? "remote" : NULL);
+ ud->is_remote_node? PCMK_VALUE_REMOTE : NULL);
}
static int
@@ -461,9 +516,10 @@ command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
xmlNode *result = NULL;
bool use_pattern = options.attr_pattern != NULL;
- CRM_LOG_ASSERT(options.type != NULL);
- CRM_LOG_ASSERT(options.attr_name != NULL);
- CRM_LOG_ASSERT(options.attr_value != NULL);
+ /* @COMPAT When we drop default support for expansion in crm_attribute,
+ * guard with `if (options.score_update)`
+ */
+ cib__set_call_options(cib_opts, crm_system_name, cib_score_update);
/* See the comment in command_query regarding xpath and regular expressions. */
if (use_pattern) {
@@ -479,16 +535,12 @@ command_update(pcmk__output_t *out, cib_t *cib, int is_remote_node)
rc = pcmk__xe_foreach_child(result, NULL, update_attr_on_node, &ud);
- if (rc != pcmk_rc_ok) {
- goto done_updating;
- }
-
} else {
rc = cib__update_node_attr(out, cib, cib_opts, options.type,
options.dest_node, options.set_type,
options.set_name, options.attr_id,
- options.attr_name, options.attr_value,
- NULL, is_remote_node ? "remote" : NULL);
+ options.attr_name, options.attr_value, NULL,
+ is_remote_node? PCMK_VALUE_REMOTE : NULL);
}
done_updating:
@@ -507,9 +559,8 @@ output_one_attribute(xmlNode *node, void *userdata)
{
struct output_data_s *od = (struct output_data_s *) userdata;
- const char *name = crm_element_value(node, XML_NVPAIR_ATTR_NAME);
- const char *value = crm_element_value(node, XML_NVPAIR_ATTR_VALUE);
- const char *host = crm_element_value(node, PCMK__XA_ATTR_NODE_NAME);
+ const char *name = crm_element_value(node, PCMK_XA_NAME);
+ const char *value = crm_element_value(node, PCMK_XA_VALUE);
const char *type = options.type;
const char *attr_id = options.attr_id;
@@ -518,7 +569,8 @@ output_one_attribute(xmlNode *node, void *userdata)
return pcmk_rc_ok;
}
- od->out->message(od->out, "attribute", type, attr_id, name, value, host);
+ od->out->message(od->out, "attribute", type, attr_id, name, value, NULL,
+ od->out->quiet, true);
od->did_output = true;
crm_info("Read %s='%s' %s%s",
pcmk__s(name, "<null>"), pcmk__s(value, ""),
@@ -556,10 +608,9 @@ command_query(pcmk__output_t *out, cib_t *cib)
const char *attr_id = options.attr_id;
const char *attr_name = options.attr_name;
const char *attr_default = options.attr_default;
- const char *dest_uname = options.dest_uname;
out->message(out, "attribute", type, attr_id, attr_name, attr_default,
- dest_uname);
+ NULL, out->quiet, true);
rc = pcmk_rc_ok;
} else if (rc != pcmk_rc_ok) {
@@ -589,22 +640,22 @@ set_type(void)
if (options.type == NULL) {
if (options.promotion_score) {
// Updating a promotion score node attribute
- options.type = g_strdup(XML_CIB_TAG_STATUS);
+ options.type = g_strdup(PCMK_XE_STATUS);
} else if (options.dest_uname != NULL) {
// Updating some other node attribute
- options.type = g_strdup(XML_CIB_TAG_NODES);
+ options.type = g_strdup(PCMK_XE_NODES);
} else {
// Updating cluster options
- options.type = g_strdup(XML_CIB_TAG_CRMCONFIG);
+ options.type = g_strdup(PCMK_XE_CRM_CONFIG);
}
} else if (pcmk__str_eq(options.type, "reboot", pcmk__str_casei)) {
- options.type = g_strdup(XML_CIB_TAG_STATUS);
+ options.type = g_strdup(PCMK_XE_STATUS);
} else if (pcmk__str_eq(options.type, "forever", pcmk__str_casei)) {
- options.type = g_strdup(XML_CIB_TAG_NODES);
+ options.type = g_strdup(PCMK_XE_NODES);
}
}
@@ -614,14 +665,16 @@ use_attrd(void)
/* Only go through the attribute manager for transient attributes, and
* then only if we're not using a file as the CIB.
*/
- return pcmk__str_eq(options.type, XML_CIB_TAG_STATUS, pcmk__str_casei) &&
+ return pcmk__str_eq(options.type, PCMK_XE_STATUS, pcmk__str_casei) &&
getenv("CIB_file") == NULL && getenv("CIB_shadow") == NULL;
}
static bool
try_ipc_update(void)
{
- return use_attrd() && (options.command == 'D' || options.command == 'u');
+ return use_attrd()
+ && ((options.command == attr_cmd_delete)
+ || (options.command == attr_cmd_update));
}
static bool
@@ -630,13 +683,30 @@ pattern_used_correctly(void)
/* --pattern can only be used with:
* -G (query), -v (update), or -D (delete)
*/
- return options.command == 'G' || options.command == 'u' || options.command == 'D';
+ switch (options.command) {
+ case attr_cmd_delete:
+ case attr_cmd_query:
+ case attr_cmd_update:
+ return true;
+ default:
+ return false;
+ }
}
static bool
delete_used_correctly(void)
{
- return options.command != 'D' || options.attr_name != NULL || options.attr_pattern != NULL;
+ return (options.command != attr_cmd_delete)
+ || (options.attr_name != NULL)
+ || (options.attr_pattern != NULL);
+}
+
+static bool
+update_used_correctly(void)
+{
+ return (options.command != attr_cmd_update)
+ || (options.attr_name != NULL)
+ || (options.attr_pattern != NULL);
}
static GOptionContext *
@@ -664,10 +734,14 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
"\tcrm_attribute --node myhost --name location --update backoffice\n\n"
"Delete the 'location' node attribute for host 'myhost':\n\n"
"\tcrm_attribute --node myhost --name location --delete\n\n"
- "Query the value of the 'cluster-delay' cluster option:\n\n"
- "\tcrm_attribute --type crm_config --name cluster-delay --query\n\n"
- "Query value of the 'cluster-delay' cluster option and print only the value:\n\n"
- "\tcrm_attribute --type crm_config --name cluster-delay --query --quiet\n\n";
+ "Query the value of the '" PCMK_OPT_CLUSTER_DELAY
+ "' cluster option:\n\n"
+ "\tcrm_attribute --type crm_config --name "
+ PCMK_OPT_CLUSTER_DELAY " --query\n\n"
+ "Query value of the '" PCMK_OPT_CLUSTER_DELAY
+ "' cluster option and print only the value:\n\n"
+ "\tcrm_attribute --type crm_config --name "
+ PCMK_OPT_CLUSTER_DELAY " --query --quiet\n\n";
context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
pcmk__add_main_args(context, extra_prog_entries);
@@ -717,7 +791,6 @@ main(int argc, char **argv)
}
pcmk__register_lib_messages(out);
- pcmk__register_messages(out, fmt_functions);
if (args->version) {
out->version(out, false);
@@ -726,6 +799,11 @@ main(int argc, char **argv)
out->quiet = args->quiet;
+ if (options.command == attr_cmd_list) {
+ command_list(out);
+ goto done;
+ }
+
if (options.promotion_score && options.attr_name == NULL) {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
@@ -753,8 +831,8 @@ main(int argc, char **argv)
set_type();
// Use default node if not given (except for cluster options and tickets)
- if (!pcmk__strcase_any_of(options.type, XML_CIB_TAG_CRMCONFIG, XML_CIB_TAG_TICKETS,
- NULL)) {
+ if (!pcmk__strcase_any_of(options.type,
+ PCMK_XE_CRM_CONFIG, PCMK_XE_TICKETS, NULL)) {
/* If we are being called from a resource agent via the cluster,
* the correct local node name will be passed as an environment
* variable. Otherwise, we have to ask the cluster.
@@ -762,8 +840,23 @@ main(int argc, char **argv)
const char *target = pcmk__node_attr_target(options.dest_uname);
if (target != NULL) {
- g_free(options.dest_uname);
- options.dest_uname = g_strdup(target);
+ /* If options.dest_uname is "auto" or "localhost", then
+ * pcmk__node_attr_target() may return it, depending on environment
+ * variables. In that case, attribute lookups will fail for "auto"
+ * (unless there's a node named "auto"). attrd maps "localhost" to
+ * the true local node name for queries.
+ *
+ * @TODO
+ * * Investigate whether "localhost" is mapped to a real node name
+ * for non-query commands. If not, possibly modify it so that it
+ * is.
+ * * Map "auto" to "localhost" (probably).
+ */
+ if (target != (const char *) options.dest_uname) {
+ g_free(options.dest_uname);
+ options.dest_uname = g_strdup(target);
+ }
+
} else if (getenv("CIB_file") != NULL && options.dest_uname == NULL) {
get_node_name_from_local();
}
@@ -800,6 +893,13 @@ main(int argc, char **argv)
goto done;
}
+ if (!update_used_correctly()) {
+ exit_code = CRM_EX_USAGE;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Error: must specify attribute name or pattern to update");
+ goto done;
+ }
+
if (options.attr_pattern) {
if (options.attr_name) {
exit_code = CRM_EX_USAGE;
@@ -824,20 +924,26 @@ main(int argc, char **argv)
options.attr_options |= pcmk__node_attr_remote;
}
- if (pcmk__str_eq(options.set_type, XML_TAG_UTILIZATION, pcmk__str_none)) {
+ if (pcmk__str_eq(options.set_type, PCMK_XE_UTILIZATION, pcmk__str_none)) {
options.attr_options |= pcmk__node_attr_utilization;
}
if (try_ipc_update() &&
(send_attrd_update(options.command, options.dest_uname, options.attr_name,
options.attr_value, options.set_name, NULL, options.attr_options) == pcmk_rc_ok)) {
+
+ const char *update = options.attr_value;
+
+ if (options.command == attr_cmd_delete) {
+ update = "<none>";
+ }
crm_info("Update %s=%s sent via pacemaker-attrd",
- options.attr_name, ((options.command == 'D')? "<none>" : options.attr_value));
+ options.attr_name, update);
- } else if (options.command == 'D') {
+ } else if (options.command == attr_cmd_delete) {
rc = command_delete(out, the_cib);
- } else if (options.command == 'u') {
+ } else if (options.command == attr_cmd_update) {
rc = command_update(out, the_cib, is_remote_node);
} else {
diff --git a/tools/crm_diff.c b/tools/crm_diff.c
index 9925ea7..09e24c1 100644
--- a/tools/crm_diff.c
+++ b/tools/crm_diff.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2023 the Pacemaker project contributors
+ * Copyright 2005-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -18,7 +18,6 @@
#include <sys/types.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml.h>
@@ -106,10 +105,12 @@ patch_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **
static void
print_patch(xmlNode *patch)
{
- char *buffer = dump_xml_formatted(patch);
+ GString *buffer = g_string_sized_new(1024);
- printf("%s", buffer);
- free(buffer);
+ pcmk__xml_string(patch, pcmk__xml_fmt_pretty, buffer, 0);
+
+ printf("%s", buffer->str);
+ g_string_free(buffer, TRUE);
fflush(stdout);
}
@@ -117,7 +118,7 @@ print_patch(xmlNode *patch)
static int
apply_patch(xmlNode *input, xmlNode *patch, gboolean as_cib)
{
- xmlNode *output = copy_xml(input);
+ xmlNode *output = pcmk__xml_copy(NULL, input);
int rc = xml_apply_patchset(output, patch, as_cib);
rc = pcmk_legacy2rc(rc);
@@ -133,7 +134,7 @@ apply_patch(xmlNode *input, xmlNode *patch, gboolean as_cib)
print_patch(output);
- version = crm_element_value(output, XML_ATTR_CRM_VERSION);
+ version = crm_element_value(output, PCMK_XA_CRM_FEATURE_SET);
buffer = calculate_xml_versioned_digest(output, FALSE, TRUE, version);
crm_trace("Digest: %s", pcmk__s(buffer, "<null>\n"));
free(buffer);
@@ -153,7 +154,7 @@ log_patch_cib_versions(xmlNode *patch)
xml_patch_versions(patch, add, del);
fmt = crm_element_value(patch, PCMK_XA_FORMAT);
- digest = crm_element_value(patch, XML_ATTR_DIGEST);
+ digest = crm_element_value(patch, PCMK__XA_DIGEST);
if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
crm_info("Patch: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
@@ -168,7 +169,8 @@ strip_patch_cib_version(xmlNode *patch, const char **vfields, size_t nvfields)
crm_element_value_int(patch, PCMK_XA_FORMAT, &format);
if (format == 2) {
- xmlNode *version_xml = find_xml_node(patch, "version", FALSE);
+ xmlNode *version_xml = pcmk__xe_first_child(patch, PCMK_XE_VERSION,
+ NULL, NULL);
if (version_xml) {
free_xml(version_xml);
@@ -178,24 +180,24 @@ strip_patch_cib_version(xmlNode *patch, const char **vfields, size_t nvfields)
int i = 0;
const char *tags[] = {
- XML_TAG_DIFF_REMOVED,
- XML_TAG_DIFF_ADDED,
+ PCMK__XE_DIFF_REMOVED,
+ PCMK__XE_DIFF_ADDED,
};
for (i = 0; i < PCMK__NELEM(tags); i++) {
xmlNode *tmp = NULL;
int lpc;
- tmp = find_xml_node(patch, tags[i], FALSE);
+ tmp = pcmk__xe_first_child(patch, tags[i], NULL, NULL);
if (tmp) {
for (lpc = 0; lpc < nvfields; lpc++) {
- xml_remove_prop(tmp, vfields[lpc]);
+ pcmk__xe_remove_attr(tmp, vfields[lpc]);
}
- tmp = find_xml_node(tmp, XML_TAG_CIB, FALSE);
+ tmp = pcmk__xe_first_child(tmp, PCMK_XE_CIB, NULL, NULL);
if (tmp) {
for (lpc = 0; lpc < nvfields; lpc++) {
- xml_remove_prop(tmp, vfields[lpc]);
+ pcmk__xe_remove_attr(tmp, vfields[lpc]);
}
}
}
@@ -209,9 +211,9 @@ generate_patch(xmlNode *object_1, xmlNode *object_2, const char *xml_file_2,
gboolean as_cib, gboolean no_version)
{
const char *vfields[] = {
- XML_ATTR_GENERATION_ADMIN,
- XML_ATTR_GENERATION,
- XML_ATTR_NUMUPDATES,
+ PCMK_XA_ADMIN_EPOCH,
+ PCMK_XA_EPOCH,
+ PCMK_XA_NUM_UPDATES,
};
xmlNode *output = NULL;
@@ -328,25 +330,25 @@ main(int argc, char **argv)
}
if (options.raw_1) {
- object_1 = string2xml(options.xml_file_1);
+ object_1 = pcmk__xml_parse(options.xml_file_1);
} else if (options.use_stdin) {
fprintf(stderr, "Input first XML fragment:");
- object_1 = stdin2xml();
+ object_1 = pcmk__xml_read(NULL);
} else if (options.xml_file_1 != NULL) {
- object_1 = filename2xml(options.xml_file_1);
+ object_1 = pcmk__xml_read(options.xml_file_1);
}
if (options.raw_2) {
- object_2 = string2xml(options.xml_file_2);
+ object_2 = pcmk__xml_parse(options.xml_file_2);
} else if (options.use_stdin) {
fprintf(stderr, "Input second XML fragment:");
- object_2 = stdin2xml();
+ object_2 = pcmk__xml_read(NULL);
} else if (options.xml_file_2 != NULL) {
- object_2 = filename2xml(options.xml_file_2);
+ object_2 = pcmk__xml_read(options.xml_file_2);
}
if (object_1 == NULL) {
diff --git a/tools/crm_error.c b/tools/crm_error.c
index 8911eae..51a0051 100644
--- a/tools/crm_error.c
+++ b/tools/crm_error.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the Pacemaker project contributors
+ * Copyright 2012-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -8,7 +8,7 @@
*/
#include <crm_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/strings_internal.h>
diff --git a/tools/crm_mon.c b/tools/crm_mon.c
index dbe76fc..85be8dc 100644
--- a/tools/crm_mon.c
+++ b/tools/crm_mon.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -26,7 +26,6 @@
#include <signal.h>
#include <sys/utsname.h>
-#include <crm/msg_xml.h>
#include <crm/services.h>
#include <crm/lrmd.h>
#include <crm/common/cmdline_internal.h>
@@ -45,7 +44,7 @@
#include <crm/pengine/internal.h>
#include <pacemaker-internal.h>
#include <crm/stonith-ng.h>
-#include <crm/fencing/internal.h>
+#include <crm/fencing/internal.h> // stonith__*
#include "crm_mon.h"
@@ -121,19 +120,20 @@ crm_mon_disconnected_html(pcmk__output_t *out, va_list args)
out->reset(out);
}
- pcmk__output_create_xml_text_node(out, "span", "Not connected to CIB");
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN,
+ "Not connected to CIB");
if (desc != NULL) {
- pcmk__output_create_xml_text_node(out, "span", ": ");
- pcmk__output_create_xml_text_node(out, "span", desc);
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, ": ");
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, desc);
}
if (state != pcmk_pacemakerd_state_invalid) {
const char *state_s = pcmk__pcmkd_state_enum2friendly(state);
- pcmk__output_create_xml_text_node(out, "span", " (");
- pcmk__output_create_xml_text_node(out, "span", state_s);
- pcmk__output_create_xml_text_node(out, "span", ")");
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, " (");
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, state_s);
+ pcmk__output_create_xml_text_node(out, PCMK__XE_SPAN, ")");
}
out->finish(out, CRM_EX_DISCONNECT, true, NULL);
@@ -185,9 +185,9 @@ crm_mon_disconnected_xml(pcmk__output_t *out, va_list args)
state_s = pcmk_pacemakerd_api_daemon_state_enum2text(state);
}
- pcmk__output_create_xml_node(out, "crm-mon-disconnected",
- XML_ATTR_DESC, desc,
- "pacemakerd-state", state_s,
+ pcmk__output_create_xml_node(out, PCMK_XE_CRM_MON_DISCONNECTED,
+ PCMK_XA_DESCRIPTION, desc,
+ PCMK_XA_PACEMAKERD_STATE, state_s,
NULL);
out->finish(out, CRM_EX_DISCONNECT, true, NULL);
@@ -284,7 +284,7 @@ struct {
{ "dc", pcmk_section_dc },
{ "failcounts", pcmk_section_failcounts },
{ "failures", pcmk_section_failures },
- { PCMK__VALUE_FENCING, pcmk_section_fencing_all },
+ { PCMK_VALUE_FENCING, pcmk_section_fencing_all },
{ "fencing-failed", pcmk_section_fence_failed },
{ "fencing-pending", pcmk_section_fence_pending },
{ "fencing-succeeded", pcmk_section_fence_worked },
@@ -322,7 +322,7 @@ apply_exclude(const gchar *excludes, GError **error) {
if (pcmk__str_eq(*s, "all", pcmk__str_none)) {
show = 0;
- } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) {
+ } else if (pcmk__str_eq(*s, PCMK_VALUE_NONE, pcmk__str_none)) {
show = all_includes(output_format);
} else if (bit != 0) {
show &= ~bit;
@@ -331,7 +331,7 @@ apply_exclude(const gchar *excludes, GError **error) {
"--exclude options: all, attributes, bans, counts, dc, "
"failcounts, failures, fencing, fencing-failed, "
"fencing-pending, fencing-succeeded, maint-mode, nodes, "
- PCMK__VALUE_NONE ", operations, options, resources, "
+ PCMK_VALUE_NONE ", operations, options, resources, "
"stack, summary, tickets, times");
result = FALSE;
break;
@@ -362,19 +362,19 @@ apply_include(const gchar *includes, GError **error) {
if (strlen(*s) > 4 && (*s)[4] == ':') {
options.neg_location_prefix = strdup(*s+5);
}
- } else if (pcmk__str_any_of(*s, "default", "defaults", NULL)) {
+ } else if (pcmk__str_any_of(*s, PCMK_VALUE_DEFAULT, "defaults", NULL)) {
show |= default_includes(output_format);
- } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) {
+ } else if (pcmk__str_eq(*s, PCMK_VALUE_NONE, pcmk__str_none)) {
show = 0;
} else if (bit != 0) {
show |= bit;
} else {
g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
"--include options: all, attributes, bans[:PREFIX], counts, dc, "
- "default, failcounts, failures, fencing, fencing-failed, "
- "fencing-pending, fencing-succeeded, maint-mode, nodes, "
- PCMK__VALUE_NONE ", operations, options, resources, "
- "stack, summary, tickets, times");
+ PCMK_VALUE_DEFAULT ", failcounts, failures, fencing, "
+ "fencing-failed, fencing-pending, fencing-succeeded, "
+ "maint-mode, nodes, " PCMK_VALUE_NONE ", operations, "
+ "options, resources, stack, summary, tickets, times");
result = FALSE;
break;
}
@@ -471,13 +471,13 @@ fence_history_cb(const gchar *option_name, const gchar *optarg, gpointer data, G
case 3:
options.fence_connect = TRUE;
fence_history = pcmk__fence_history_full;
- return include_exclude_cb("--include", PCMK__VALUE_FENCING, data,
+ return include_exclude_cb("--include", PCMK_VALUE_FENCING, data,
err);
case 2:
options.fence_connect = TRUE;
fence_history = pcmk__fence_history_full;
- return include_exclude_cb("--include", PCMK__VALUE_FENCING, data,
+ return include_exclude_cb("--include", PCMK_VALUE_FENCING, data,
err);
case 1:
@@ -488,7 +488,7 @@ fence_history_cb(const gchar *option_name, const gchar *optarg, gpointer data, G
case 0:
options.fence_connect = FALSE;
fence_history = pcmk__fence_history_none;
- return include_exclude_cb("--exclude", PCMK__VALUE_FENCING, data,
+ return include_exclude_cb("--exclude", PCMK_VALUE_FENCING, data,
err);
default:
@@ -553,7 +553,7 @@ reconnect_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErro
g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid value for -i: %s", optarg);
return FALSE;
} else {
- options.reconnect_ms = crm_parse_interval_spec(optarg);
+ pcmk_parse_interval_spec(optarg, &options.reconnect_ms);
if (options.exec_mode != mon_exec_daemonized) {
// Reconnect interval applies to daemonized too, so don't override
@@ -756,7 +756,7 @@ static GOptionEntry display_entries[] = {
NULL },
{ "pending", 'j', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.print_pending,
- "Display pending state if 'record-pending' is enabled",
+ "Display pending state if '" PCMK_META_RECORD_PENDING "' is enabled",
NULL },
{ NULL }
@@ -854,8 +854,12 @@ mon_cib_connection_destroy(gpointer user_data)
/* the client API won't properly reconnect notifications if they are still
* in the table - so remove them
*/
- stonith_api_delete(st);
- st = NULL;
+ if (st != NULL) {
+ if (st->state != stonith_disconnected) {
+ st->cmds->disconnect(st);
+ }
+ st->cmds->remove_notification(st, NULL);
+ }
if (cib) {
cib->cmds->signoff(cib);
@@ -918,13 +922,17 @@ setup_fencer_connection(void)
if (rc == pcmk_ok) {
crm_trace("Setting up stonith callbacks");
if (options.watch_fencing) {
- st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
+ st->cmds->register_notification(st,
+ PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ mon_st_callback_event);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_FENCE,
mon_st_callback_event);
- st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback_event);
} else {
- st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
+ st->cmds->register_notification(st,
+ PCMK__VALUE_ST_NOTIFY_DISCONNECT,
+ mon_st_callback_display);
+ st->cmds->register_notification(st, PCMK__VALUE_ST_NOTIFY_HISTORY,
mon_st_callback_display);
- st->cmds->register_notification(st, T_STONITH_NOTIFY_HISTORY, mon_st_callback_display);
}
} else {
stonith_api_delete(st);
@@ -960,10 +968,11 @@ setup_cib_connection(void)
}
if (rc == pcmk_rc_ok) {
- cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY,
+ cib->cmds->del_notify_callback(cib, PCMK__VALUE_CIB_DIFF_NOTIFY,
crm_diff_update);
- rc = pcmk_legacy2rc(cib->cmds->add_notify_callback(cib,
- T_CIB_DIFF_NOTIFY, crm_diff_update));
+ rc = cib->cmds->add_notify_callback(cib, PCMK__VALUE_CIB_DIFF_NOTIFY,
+ crm_diff_update);
+ rc = pcmk_legacy2rc(rc);
}
if (rc != pcmk_rc_ok) {
@@ -1088,6 +1097,28 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_dat
{
int c;
gboolean config_mode = FALSE;
+ gboolean rc = G_SOURCE_CONTINUE;
+
+ /* If the attached pty device (pseudo-terminal) has been closed/deleted,
+ * the condition (G_IO_IN | G_IO_ERR | G_IO_HUP) occurs.
+ * Exit with an error, otherwise the process would persist in the
+ * background and significantly raise the CPU usage.
+ */
+ if ((condition & G_IO_ERR) && (condition & G_IO_HUP)) {
+ rc = G_SOURCE_REMOVE;
+ clean_up(CRM_EX_IOERR);
+ }
+
+ /* The connection/fd has been closed. Refresh the screen and remove this
+ * event source hence ignore stdin.
+ */
+ if (condition & (G_IO_HUP | G_IO_NVAL)) {
+ rc = G_SOURCE_REMOVE;
+ }
+
+ if ((condition & G_IO_IN) == 0) {
+ return rc;
+ }
while (1) {
@@ -1194,7 +1225,7 @@ detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_dat
refresh:
refresh_after_event(FALSE, TRUE);
- return TRUE;
+ return rc;
}
#endif // CURSES_ENABLED
@@ -1313,27 +1344,11 @@ static void
add_output_args(void) {
GError *err = NULL;
- if (output_format == mon_output_plain) {
- if (!pcmk__force_args(context, &err, "%s --text-fancy", g_get_prgname())) {
- g_propagate_error(&error, err);
- clean_up(CRM_EX_USAGE);
- }
- } else if (output_format == mon_output_cgi) {
+ if (output_format == mon_output_cgi) {
if (!pcmk__force_args(context, &err, "%s --html-cgi", g_get_prgname())) {
g_propagate_error(&error, err);
clean_up(CRM_EX_USAGE);
}
- } else if (output_format == mon_output_xml) {
- if (!pcmk__force_args(context, &err, "%s --xml-simple-list --xml-substitute", g_get_prgname())) {
- g_propagate_error(&error, err);
- clean_up(CRM_EX_USAGE);
- }
- } else if (output_format == mon_output_legacy_xml) {
- output_format = mon_output_xml;
- if (!pcmk__force_args(context, &err, "%s --xml-legacy --xml-substitute", g_get_prgname())) {
- g_propagate_error(&error, err);
- clean_up(CRM_EX_USAGE);
- }
}
}
@@ -1359,7 +1374,7 @@ reconcile_output_format(pcmk__common_args_t *args)
return;
}
- if (pcmk__str_eq(args->output_ty, "none", pcmk__str_none)) {
+ if (pcmk__str_eq(args->output_ty, PCMK_VALUE_NONE, pcmk__str_none)) {
output_format = mon_output_none;
} else if (pcmk__str_eq(args->output_ty, "html", pcmk__str_none)) {
@@ -1582,8 +1597,6 @@ main(int argc, char **argv)
set_default_exec_mode(args);
add_output_args();
- /* output_format MUST NOT BE CHANGED AFTER THIS POINT. */
-
rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
if (rc != pcmk_rc_ok) {
g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error creating output format %s: %s",
@@ -1591,11 +1604,22 @@ main(int argc, char **argv)
return clean_up(CRM_EX_ERROR);
}
+ if (output_format == mon_output_legacy_xml) {
+ output_format = mon_output_xml;
+ pcmk__output_set_legacy_xml(out);
+ }
+
+ /* output_format MUST NOT BE CHANGED AFTER THIS POINT. */
+
/* If we had a valid format for pcmk__output_new(), output_format should be
* set by now.
*/
CRM_ASSERT(output_format != mon_output_unset);
+ if (output_format == mon_output_plain) {
+ pcmk__output_text_set_fancy(out, true);
+ }
+
if (options.exec_mode == mon_exec_daemonized) {
if (!options.external_agent && (output_format == mon_output_none)) {
g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
@@ -1670,10 +1694,16 @@ main(int argc, char **argv)
show_opts |= pcmk_show_inactive_rscs | pcmk_show_timing;
}
- if ((output_format == mon_output_html || output_format == mon_output_cgi) &&
- out->dest != stdout) {
- pcmk__html_add_header("meta", "http-equiv", "refresh", "content",
- pcmk__itoa(options.reconnect_ms / 1000), NULL);
+ if ((output_format == mon_output_html || output_format == mon_output_cgi)
+ && (out->dest != stdout)) {
+
+ char *content = pcmk__itoa(options.reconnect_ms / 1000);
+
+ pcmk__html_add_header(PCMK__XE_META,
+ PCMK__XA_HTTP_EQUIV, PCMK__VALUE_REFRESH,
+ PCMK__XA_CONTENT, content,
+ NULL);
+ free(content);
}
#ifdef PCMK__COMPAT_2_0
@@ -1731,7 +1761,8 @@ main(int argc, char **argv)
ncurses_winch_handler = NULL;
io_channel = g_io_channel_unix_new(STDIN_FILENO);
- g_io_add_watch(io_channel, G_IO_IN, detect_user_input, NULL);
+ g_io_add_watch(io_channel, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+ detect_user_input, NULL);
}
#endif
@@ -1743,10 +1774,6 @@ main(int argc, char **argv)
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
- if (io_channel != NULL) {
- g_io_channel_shutdown(io_channel, TRUE, NULL);
- }
-
crm_info("Exiting %s", crm_system_name);
return clean_up(CRM_EX_OK);
@@ -1785,7 +1812,7 @@ send_custom_trap(const char *node, const char *rsc, const char *task, int target
if (pid == 0) {
/* crm_debug("notification: I am the child. Executing the nofitication program."); */
execl(options.external_agent, options.external_agent, NULL);
- exit(CRM_EX_ERROR);
+ crm_exit(CRM_EX_ERROR);
}
crm_trace("Finished running custom notification program '%s'.", options.external_agent);
@@ -1814,14 +1841,14 @@ handle_rsc_op(xmlNode *xml, void *userdata)
xmlNode *n = xml;
xmlNode * rsc_op = xml;
- if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
+ if(strcmp((const char*)xml->name, PCMK__XE_LRM_RSC_OP) != 0) {
pcmk__xe_foreach_child(xml, NULL, handle_rsc_op, (void *) node_id);
return pcmk_rc_ok;
}
- id = pe__xe_history_key(rsc_op);
+ id = pcmk__xe_history_key(rsc_op);
- magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
+ magic = crm_element_value(rsc_op, PCMK__XA_TRANSITION_MAGIC);
if (magic == NULL) {
/* non-change */
return pcmk_rc_ok;
@@ -1838,18 +1865,18 @@ handle_rsc_op(xmlNode *xml, void *userdata)
goto bail;
}
- node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
+ node = crm_element_value(rsc_op, PCMK__META_ON_NODE);
- while ((n != NULL) && !pcmk__xe_is(n, XML_CIB_TAG_STATE)) {
+ while ((n != NULL) && !pcmk__xe_is(n, PCMK__XE_NODE_STATE)) {
n = n->parent;
}
if(node == NULL && n) {
- node = crm_element_value(n, XML_ATTR_UNAME);
+ node = crm_element_value(n, PCMK_XA_UNAME);
}
if (node == NULL && n) {
- node = ID(n);
+ node = pcmk__xe_id(n);
}
if (node == NULL) {
@@ -1902,101 +1929,107 @@ mon_trigger_refresh(gpointer user_data)
static int
handle_op_for_node(xmlNode *xml, void *userdata)
{
- const char *node = crm_element_value(xml, XML_ATTR_UNAME);
+ const char *node = crm_element_value(xml, PCMK_XA_UNAME);
if (node == NULL) {
- node = ID(xml);
+ node = pcmk__xe_id(xml);
}
handle_rsc_op(xml, (void *) node);
return pcmk_rc_ok;
}
-static void
-crm_diff_update_v2(const char *event, xmlNode * msg)
+static int
+crm_diff_update_element_v2(xmlNode *change, void *userdata)
{
- xmlNode *change = NULL;
- xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
-
- for (change = pcmk__xml_first_child(diff); change != NULL;
- change = pcmk__xml_next(change)) {
- const char *name = NULL;
- const char *op = crm_element_value(change, XML_DIFF_OP);
- const char *xpath = crm_element_value(change, XML_DIFF_PATH);
- xmlNode *match = NULL;
- const char *node = NULL;
-
- if(op == NULL) {
- continue;
-
- } else if(strcmp(op, "create") == 0) {
- match = change->children;
+ const char *name = NULL;
+ const char *op = crm_element_value(change, PCMK_XA_OPERATION);
+ const char *xpath = crm_element_value(change, PCMK_XA_PATH);
+ xmlNode *match = NULL;
+ const char *node = NULL;
- } else if(strcmp(op, "move") == 0) {
- continue;
+ if (op == NULL) {
+ return pcmk_rc_ok;
- } else if(strcmp(op, "delete") == 0) {
- continue;
+ } else if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
+ match = change->children;
- } else if(strcmp(op, "modify") == 0) {
- match = first_named_child(change, XML_DIFF_RESULT);
- if(match) {
- match = match->children;
- }
- }
+ } else if (pcmk__str_any_of(op, PCMK_VALUE_MOVE, PCMK_VALUE_DELETE,
+ NULL)) {
+ return pcmk_rc_ok;
+ } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
+ match = pcmk__xe_first_child(change, PCMK_XE_CHANGE_RESULT, NULL, NULL);
if(match) {
- name = (const char *)match->name;
+ match = match->children;
}
+ }
- crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
- if(xpath == NULL) {
- /* Version field, ignore */
+ if(match) {
+ name = (const char *)match->name;
+ }
- } else if(name == NULL) {
- crm_debug("No result for %s operation to %s", op, xpath);
- CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
+ crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
+ if(xpath == NULL) {
+ /* Version field, ignore */
- } else if(strcmp(name, XML_TAG_CIB) == 0) {
- pcmk__xe_foreach_child(first_named_child(match, XML_CIB_TAG_STATUS),
- NULL, handle_op_for_node, NULL);
+ } else if(name == NULL) {
+ crm_debug("No result for %s operation to %s", op, xpath);
+ CRM_ASSERT(pcmk__str_any_of(op, PCMK_VALUE_MOVE, PCMK_VALUE_DELETE,
+ NULL));
- } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
- pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL);
+ } else if (strcmp(name, PCMK_XE_CIB) == 0) {
+ pcmk__xe_foreach_child(pcmk__xe_first_child(match, PCMK_XE_STATUS, NULL,
+ NULL),
+ NULL, handle_op_for_node, NULL);
- } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
- node = crm_element_value(match, XML_ATTR_UNAME);
- if (node == NULL) {
- node = ID(match);
- }
- handle_rsc_op(match, (void *) node);
+ } else if (strcmp(name, PCMK_XE_STATUS) == 0) {
+ pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL);
- } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
- node = ID(match);
- handle_rsc_op(match, (void *) node);
+ } else if (strcmp(name, PCMK__XE_NODE_STATE) == 0) {
+ node = crm_element_value(match, PCMK_XA_UNAME);
+ if (node == NULL) {
+ node = pcmk__xe_id(match);
+ }
+ handle_rsc_op(match, (void *) node);
- } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ } else if (strcmp(name, PCMK__XE_LRM) == 0) {
+ node = pcmk__xe_id(match);
+ handle_rsc_op(match, (void *) node);
- handle_rsc_op(match, local_node);
- free(local_node);
+ } else if (strcmp(name, PCMK__XE_LRM_RESOURCES) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ handle_rsc_op(match, local_node);
+ free(local_node);
- handle_rsc_op(match, local_node);
- free(local_node);
+ } else if (strcmp(name, PCMK__XE_LRM_RESOURCE) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
- char *local_node = pcmk__xpath_node_id(xpath, "lrm");
+ handle_rsc_op(match, local_node);
+ free(local_node);
- handle_rsc_op(match, local_node);
- free(local_node);
+ } else if (strcmp(name, PCMK__XE_LRM_RSC_OP) == 0) {
+ char *local_node = pcmk__xpath_node_id(xpath, PCMK__XE_LRM);
- } else {
- crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
- }
+ handle_rsc_op(match, local_node);
+ free(local_node);
+
+ } else {
+ crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
}
+
+ return pcmk_rc_ok;
+}
+
+static void
+crm_diff_update_v2(const char *event, xmlNode * msg)
+{
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
+ NULL, NULL);
+ xmlNode *diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
+
+ pcmk__xe_foreach_child(diff, NULL, crm_diff_update_element_v2, NULL);
}
static void
@@ -2004,8 +2037,9 @@ crm_diff_update_v1(const char *event, xmlNode * msg)
{
/* Process operation updates */
xmlXPathObject *xpathObj = xpath_search(msg,
- "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
- "//" XML_LRM_TAG_RSC_OP);
+ "//" PCMK__XE_CIB_UPDATE_RESULT
+ "//" PCMK__XE_DIFF_ADDED
+ "//" PCMK__XE_LRM_RSC_OP);
int lpc = 0, max = numXpathResults(xpathObj);
for (lpc = 0; lpc < max; lpc++) {
@@ -2022,7 +2056,9 @@ crm_diff_update(const char *event, xmlNode * msg)
int rc = -1;
static bool stale = FALSE;
gboolean cib_updated = FALSE;
- xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
+ xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CIB_UPDATE_RESULT,
+ NULL, NULL);
+ xmlNode *diff = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
out->progress(out, false);
@@ -2218,6 +2254,10 @@ clean_up(crm_exit_t exit_code)
/* Quitting crm_mon is much more complicated than it ought to be. */
/* (1) Close connections, free things, etc. */
+ if (io_channel != NULL) {
+ g_io_channel_shutdown(io_channel, TRUE, NULL);
+ }
+
cib__clean_up_connection(&cib);
stonith_api_delete(st);
free(options.neg_location_prefix);
diff --git a/tools/crm_mon.h b/tools/crm_mon.h
index c87432d..2228add 100644
--- a/tools/crm_mon.h
+++ b/tools/crm_mon.h
@@ -71,8 +71,7 @@ void curses_formatted_vprintf(pcmk__output_t *out, const char *format, va_list a
void curses_indented_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
void curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0);
-extern GOptionEntry crm_mon_curses_output_entries[];
-#define CRM_MON_SUPPORTED_FORMAT_CURSES { "console", crm_mon_mk_curses_output, crm_mon_curses_output_entries }
+#define CRM_MON_SUPPORTED_FORMAT_CURSES { "console", crm_mon_mk_curses_output, NULL }
#endif
#endif
diff --git a/tools/crm_mon_curses.c b/tools/crm_mon_curses.c
index 212a400..4fc09ab 100644
--- a/tools/crm_mon_curses.c
+++ b/tools/crm_mon_curses.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2019-2023 the Pacemaker project contributors
+ * Copyright 2019-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -15,7 +15,7 @@
#include <crm/common/output.h>
#include <crm/common/cmdline_internal.h>
#include <crm/stonith-ng.h>
-#include <crm/fencing/internal.h>
+#include <crm/fencing/internal.h> // stonith__history_description()
#include <crm/pengine/internal.h>
#include <glib.h>
#include <pacemaker-internal.h>
@@ -24,10 +24,6 @@
#if CURSES_ENABLED
-GOptionEntry crm_mon_curses_output_entries[] = {
- { NULL }
-};
-
typedef struct curses_list_data_s {
unsigned int len;
char *singular_noun;
@@ -39,6 +35,14 @@ typedef struct private_data_s {
} private_data_t;
static void
+free_list_data(gpointer data) {
+ curses_list_data_t *list_data = data;
+
+ free(list_data->singular_noun);
+ free(list_data->plural_noun);
+}
+
+static void
curses_free_priv(pcmk__output_t *out) {
private_data_t *priv = NULL;
@@ -48,7 +52,7 @@ curses_free_priv(pcmk__output_t *out) {
priv = out->priv;
- g_queue_free(priv->parent_q);
+ g_queue_free_full(priv->parent_q, free_list_data);
free(priv);
out->priv = NULL;
}
@@ -201,10 +205,10 @@ curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *pl
va_end(ap);
}
- new_list = calloc(1, sizeof(curses_list_data_t));
+ new_list = pcmk__assert_alloc(1, sizeof(curses_list_data_t));
new_list->len = 0;
- pcmk__str_update(&new_list->singular_noun, singular_noun);
- pcmk__str_update(&new_list->plural_noun, plural_noun);
+ new_list->singular_noun = pcmk__str_copy(singular_noun);
+ new_list->plural_noun = pcmk__str_copy(plural_noun);
g_queue_push_tail(priv->parent_q, new_list);
}
@@ -262,7 +266,7 @@ curses_end_list(pcmk__output_t *out) {
}
}
- free(node);
+ free_list_data(node);
}
static bool
@@ -310,7 +314,7 @@ curses_prompt(const char *prompt, bool do_echo, char **dest)
free(*dest);
}
- *dest = calloc(1, 1024);
+ *dest = pcmk__assert_alloc(1, 1024);
/* On older systems, scanw is defined as taking a char * for its first argument,
* while newer systems rightly want a const char *. Accomodate both here due
* to building with -Werror.
diff --git a/tools/crm_node.c b/tools/crm_node.c
index 1e7ce6c..3a8d2d1 100644
--- a/tools/crm_node.c
+++ b/tools/crm_node.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -19,11 +19,11 @@
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/mainloop.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/cib.h>
#include <crm/cib/internal.h>
#include <crm/common/ipc_controld.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <pacemaker-internal.h>
@@ -159,17 +159,17 @@ node_id_xml(pcmk__output_t *out, va_list args) {
char *id_s = crm_strdup_printf("%" PRIu32, node_id);
- pcmk__output_create_xml_node(out, "node-info",
- "nodeid", id_s,
+ pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
+ PCMK_XA_NODEID, id_s,
NULL);
free(id_s);
return pcmk_rc_ok;
}
-PCMK__OUTPUT_ARGS("node-list", "GList *")
+PCMK__OUTPUT_ARGS("simple-node-list", "GList *")
static int
-node_list_default(pcmk__output_t *out, va_list args)
+simple_node_list_default(pcmk__output_t *out, va_list args)
{
GList *nodes = va_arg(args, GList *);
@@ -182,22 +182,22 @@ node_list_default(pcmk__output_t *out, va_list args)
return pcmk_rc_ok;
}
-PCMK__OUTPUT_ARGS("node-list", "GList *")
+PCMK__OUTPUT_ARGS("simple-node-list", "GList *")
static int
-node_list_xml(pcmk__output_t *out, va_list args)
+simple_node_list_xml(pcmk__output_t *out, va_list args)
{
GList *nodes = va_arg(args, GList *);
- out->begin_list(out, NULL, NULL, "nodes");
+ out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
pcmk_controld_api_node_t *node = node_iter->data;
char *id_s = crm_strdup_printf("%" PRIu32, node->id);
- pcmk__output_create_xml_node(out, "node",
- "id", id_s,
- "name", node->uname,
- "state", node->state,
+ pcmk__output_create_xml_node(out, PCMK_XE_NODE,
+ PCMK_XA_ID, id_s,
+ PCMK_XA_NAME, node->uname,
+ PCMK_XA_STATE, node->state,
NULL);
free(id_s);
@@ -226,9 +226,9 @@ node_name_xml(pcmk__output_t *out, va_list args) {
char *id_s = crm_strdup_printf("%" PRIu32, node_id);
- pcmk__output_create_xml_node(out, "node-info",
- "nodeid", id_s,
- XML_ATTR_UNAME, node_name,
+ pcmk__output_create_xml_node(out, PCMK_XE_NODE_INFO,
+ PCMK_XA_NODEID, id_s,
+ PCMK_XA_UNAME, node_name,
NULL);
free(id_s);
@@ -265,7 +265,7 @@ partition_list_xml(pcmk__output_t *out, va_list args)
{
GList *nodes = va_arg(args, GList *);
- out->begin_list(out, NULL, NULL, "nodes");
+ out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
for (GList *node_iter = nodes; node_iter != NULL; node_iter = node_iter->next) {
pcmk_controld_api_node_t *node = node_iter->data;
@@ -273,10 +273,10 @@ partition_list_xml(pcmk__output_t *out, va_list args)
if (pcmk__str_eq(node->state, "member", pcmk__str_none)) {
char *id_s = crm_strdup_printf("%" PRIu32, node->id);
- pcmk__output_create_xml_node(out, "node",
- "id", id_s,
- "name", node->uname,
- "state", node->state,
+ pcmk__output_create_xml_node(out, PCMK_XE_NODE,
+ PCMK_XA_ID, id_s,
+ PCMK_XA_NAME, node->uname,
+ PCMK_XA_STATE, node->state,
NULL);
free(id_s);
}
@@ -300,8 +300,8 @@ static int
quorum_xml(pcmk__output_t *out, va_list args) {
bool have_quorum = va_arg(args, int);
- pcmk__output_create_xml_node(out, "cluster-info",
- "quorum", have_quorum ? "true" : "false",
+ pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_INFO,
+ PCMK_XA_QUORUM, pcmk__btoa(have_quorum),
NULL);
return pcmk_rc_ok;
}
@@ -309,14 +309,14 @@ quorum_xml(pcmk__output_t *out, va_list args) {
static pcmk__message_entry_t fmt_functions[] = {
{ "node-id", "default", node_id_default },
{ "node-id", "xml", node_id_xml },
- { "node-list", "default", node_list_default },
- { "node-list", "xml", node_list_xml },
{ "node-name", "default", node_name_default },
{ "node-name", "xml", node_name_xml },
- { "quorum", "default", quorum_default },
- { "quorum", "xml", quorum_xml },
{ "partition-list", "default", partition_list_default },
{ "partition-list", "xml", partition_list_xml },
+ { "quorum", "default", quorum_default },
+ { "quorum", "xml", quorum_xml },
+ { "simple-node-list", "default", simple_node_list_default },
+ { "simple-node-list", "xml", simple_node_list_xml },
{ NULL, NULL, NULL }
};
@@ -374,7 +374,7 @@ controller_event_cb(pcmk_ipc_api_t *controld_api,
if (options.command == 'p') {
out->message(out, "partition-list", reply->data.nodes);
} else if (options.command == 'l') {
- out->message(out, "node-list", reply->data.nodes);
+ out->message(out, "simple-node-list", reply->data.nodes);
}
// Success
@@ -465,10 +465,11 @@ print_node_name(uint32_t nodeid)
if (nodeid == 0) {
// Check environment first (i.e. when called by resource agent)
- const char *name = getenv("OCF_RESKEY_" CRM_META "_" XML_LRM_ATTR_TARGET);
+ const char *name = getenv("OCF_RESKEY_" CRM_META "_"
+ PCMK__META_ON_NODE);
if (name != NULL) {
- rc = out->message(out, "node-name", 0, name);
+ rc = out->message(out, "node-name", 0UL, name);
goto done;
}
}
@@ -485,7 +486,7 @@ print_node_name(uint32_t nodeid)
return;
}
- rc = out->message(out, "node-name", 0, node_name);
+ rc = out->message(out, "node-name", 0UL, node_name);
done:
if (node_name != NULL) {
@@ -543,17 +544,14 @@ static int
remove_from_section(cib_t *cib, const char *element, const char *section,
const char *node_name, long node_id)
{
- xmlNode *xml = NULL;
int rc = pcmk_rc_ok;
+ xmlNode *xml = pcmk__xe_create(NULL, element);
- xml = create_xml_node(NULL, element);
- if (xml == NULL) {
- return pcmk_rc_error;
- }
- crm_xml_add(xml, XML_ATTR_UNAME, node_name);
+ crm_xml_add(xml, PCMK_XA_UNAME, node_name);
if (node_id > 0) {
crm_xml_set_id(xml, "%ld", node_id);
}
+
rc = cib->cmds->remove(cib, section, xml, cib_transaction);
free_xml(xml);
return (rc >= 0)? pcmk_rc_ok : pcmk_legacy2rc(rc);
@@ -592,10 +590,10 @@ purge_node_from_cib(const char *node_name, long node_id)
}
// Remove from configuration and status
- rc = remove_from_section(cib, XML_CIB_TAG_NODE, XML_CIB_TAG_NODES,
- node_name, node_id);
+ rc = remove_from_section(cib, PCMK_XE_NODE, PCMK_XE_NODES, node_name,
+ node_id);
if (rc == pcmk_rc_ok) {
- rc = remove_from_section(cib, XML_CIB_TAG_STATE, XML_CIB_TAG_STATUS,
+ rc = remove_from_section(cib, PCMK__XE_NODE_STATE, PCMK_XE_STATUS,
node_name, node_id);
}
@@ -693,7 +691,7 @@ purge_node_from_fencer(const char *node_name, long node_id)
if (node_id > 0) {
crm_xml_set_id(cmd, "%ld", node_id);
}
- crm_xml_add(cmd, XML_ATTR_UNAME, node_name);
+ crm_xml_add(cmd, PCMK_XA_UNAME, node_name);
rc = crm_ipc_send(conn, cmd, 0, 0, NULL);
if (rc >= 0) {
@@ -806,11 +804,6 @@ main(int argc, char **argv)
goto done;
}
- if (!pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname())) {
- exit_code = CRM_EX_SOFTWARE;
- goto done;
- }
-
if (args->version) {
out->version(out, false);
goto done;
diff --git a/tools/crm_resource.c b/tools/crm_resource.c
index 7c4a0a1..c592e86 100644
--- a/tools/crm_resource.c
+++ b/tools/crm_resource.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -18,6 +18,7 @@
#include <pacemaker-internal.h>
#include <sys/param.h>
+#include <stdint.h> // uint32_t
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
@@ -53,14 +54,15 @@ enum rsc_command {
cmd_list_all_ops,
cmd_list_alternatives,
cmd_list_instances,
+ cmd_list_options,
cmd_list_providers,
cmd_list_resources,
cmd_list_standards,
cmd_locate,
cmd_metadata,
cmd_move,
- cmd_query_raw_xml,
cmd_query_xml,
+ cmd_query_xml_raw,
cmd_refresh,
cmd_restart,
cmd_set_param,
@@ -72,18 +74,10 @@ enum rsc_command {
struct {
enum rsc_command rsc_cmd; // crm_resource command to perform
- // Infrastructure that given command needs to work
- gboolean require_cib; // Whether command requires CIB IPC
- int cib_options; // Options to use with CIB IPC calls
- gboolean require_crmd; // Whether command requires controller IPC
- gboolean require_scheduler; // Whether command requires scheduler data
- gboolean require_resource; // Whether command requires resource specified
- gboolean require_node; // Whether command requires node specified
- int find_flags; // Flags to use when searching for resource
-
// Command-line option values
gchar *rsc_id; // Value of --resource
gchar *rsc_type; // Value of --resource-type
+ gboolean all; // --all was given
gboolean force; // --force was given
gboolean clear_expired; // --expired was given
gboolean recursive; // --recursive was given
@@ -92,18 +86,19 @@ struct {
gchar *interval_spec; // Value of --interval
gchar *move_lifetime; // Value of --lifetime
gchar *operation; // Value of --operation
+ enum pcmk__opt_flags opt_list; // Parsed from --list-options
const char *attr_set_type; // Instance, meta, utilization, or element attribute
gchar *prop_id; // --nvpair (attribute XML ID)
char *prop_name; // Attribute name
gchar *prop_set; // --set-name (attribute block XML ID)
gchar *prop_value; // --parameter-value (attribute value)
- int timeout_ms; // Parsed from --timeout value
+ guint timeout_ms; // Parsed from --timeout value
char *agent_spec; // Standard and/or provider and/or agent
gchar *xml_file; // Value of (deprecated) --xml-file
int check_level; // Optional value of --validate or --force-check
// Resource configuration specified via command-line arguments
- gboolean cmdline_config; // Resource configuration was via arguments
+ bool cmdline_config; // Resource configuration was via arguments
char *v_agent; // Value of --agent
char *v_class; // Value of --class
char *v_provider; // Value of --provider
@@ -113,66 +108,17 @@ struct {
gchar **remainder; // Positional arguments as given
GHashTable *override_params; // Resource parameter values that override config
} options = {
- .attr_set_type = XML_TAG_ATTR_SETS,
+ .attr_set_type = PCMK_XE_INSTANCE_ATTRIBUTES,
.check_level = -1,
- .cib_options = cib_sync_call,
- .require_cib = TRUE,
- .require_scheduler = TRUE,
- .require_resource = TRUE,
+ .rsc_cmd = cmd_list_resources, // List all resources if no command given
};
-#if 0
-// @COMPAT @TODO enable this at next backward compatibility break
-#define SET_COMMAND(cmd) do { \
- if (options.rsc_cmd != cmd_none) { \
- g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE, \
- "Only one command option may be specified"); \
- return FALSE; \
- } \
- options.rsc_cmd = (cmd); \
- } while (0)
-#else
-#define SET_COMMAND(cmd) do { \
- if (options.rsc_cmd != cmd_none) { \
- reset_options(); \
- } \
- options.rsc_cmd = (cmd); \
- } while (0)
-#endif
-
-gboolean agent_provider_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean cleanup_refresh_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean expired_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean list_agents_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean list_providers_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean list_standards_cb(const gchar *option_name, const gchar *optarg,
+gboolean cmdline_config_cb(const gchar *option_name, const gchar *optarg,
gpointer data, GError **error);
-gboolean list_alternatives_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean metadata_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
gboolean option_cb(const gchar *option_name, const gchar *optarg,
gpointer data, GError **error);
-gboolean fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean flag_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean get_param_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean list_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean set_delete_param_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean set_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean validate_or_force_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean restart_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean digests_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error);
-gboolean wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
-gboolean why_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
static crm_exit_t exit_code = CRM_EX_OK;
static pcmk__output_t *out = NULL;
@@ -326,77 +272,266 @@ build_constraint_list(xmlNode *root)
xmlXPathObjectPtr xpathObj = NULL;
int ndx = 0;
- cib_constraints = pcmk_find_cib_element(root, XML_CIB_TAG_CONSTRAINTS);
- xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION);
+ cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
+ xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION);
for (ndx = 0; ndx < numXpathResults(xpathObj); ndx++) {
xmlNode *match = getXpathResult(xpathObj, ndx);
- retval = g_list_insert_sorted(retval, (gpointer) ID(match), compare_id);
+ retval = g_list_insert_sorted(retval, (gpointer) pcmk__xe_id(match),
+ compare_id);
}
freeXpathObject(xpathObj);
return retval;
}
+static gboolean
+validate_opt_list(const gchar *optarg)
+{
+ if (pcmk__str_eq(optarg, PCMK_VALUE_FENCING, pcmk__str_none)) {
+ options.opt_list = pcmk__opt_fencing;
+
+ } else if (pcmk__str_eq(optarg, PCMK__VALUE_PRIMITIVE, pcmk__str_none)) {
+ options.opt_list = pcmk__opt_primitive;
+
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Process options that set the command
+ *
+ * Nothing else should set \c options.rsc_cmd.
+ *
+ * \param[in] option_name Name of the option being parsed
+ * \param[in] optarg Value to be parsed
+ * \param[in] data Ignored
+ * \param[out] error Where to store recoverable error, if any
+ *
+ * \return \c TRUE if the option was successfully parsed, or \c FALSE if an
+ * error occurred, in which case \p *error is set
+ */
+static gboolean
+command_cb(const gchar *option_name, const gchar *optarg, gpointer data,
+ GError **error)
+{
+ // Sorted by enum rsc_command name
+ if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) {
+ options.rsc_cmd = cmd_ban;
+
+ } else if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) {
+ options.rsc_cmd = cmd_cleanup;
+
+ } else if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) {
+ options.rsc_cmd = cmd_clear;
+
+ } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) {
+ options.rsc_cmd = cmd_colocations;
+
+ } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) {
+ options.rsc_cmd = cmd_colocations;
+ options.recursive = TRUE;
+
+ } else if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) {
+ options.rsc_cmd = cmd_cts;
+
+ } else if (pcmk__str_any_of(option_name, "-D", "--delete", NULL)) {
+ options.rsc_cmd = cmd_delete;
+
+ } else if (pcmk__str_any_of(option_name, "-d", "--delete-parameter",
+ NULL)) {
+ options.rsc_cmd = cmd_delete_param;
+ pcmk__str_update(&options.prop_name, optarg);
+
+ } else if (pcmk__str_eq(option_name, "--digests", pcmk__str_none)) {
+ options.rsc_cmd = cmd_digests;
+
+ if (options.override_params == NULL) {
+ options.override_params = pcmk__strkey_table(free, free);
+ }
+
+ } else if (pcmk__str_any_of(option_name,
+ "--force-demote", "--force-promote",
+ "--force-start", "--force-stop",
+ "--force-check", "--validate", NULL)) {
+ options.rsc_cmd = cmd_execute_agent;
+
+ g_free(options.operation);
+ options.operation = g_strdup(option_name + 2); // skip "--"
+
+ if (options.override_params == NULL) {
+ options.override_params = pcmk__strkey_table(free, free);
+ }
+
+ if (optarg != NULL) {
+ if (pcmk__scan_min_int(optarg, &options.check_level,
+ 0) != pcmk_rc_ok) {
+ g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
+ _("Invalid check level setting: %s"), optarg);
+ return FALSE;
+ }
+ }
+
+ } else if (pcmk__str_any_of(option_name, "-F", "--fail", NULL)) {
+ options.rsc_cmd = cmd_fail;
+
+ } else if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) {
+ options.rsc_cmd = cmd_get_param;
+ pcmk__str_update(&options.prop_name, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-G", "--get-property", NULL)) {
+ options.rsc_cmd = cmd_get_property;
+ pcmk__str_update(&options.prop_name, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) {
+ options.rsc_cmd = cmd_list_active_ops;
+
+ } else if (pcmk__str_eq(option_name, "--list-agents", pcmk__str_none)) {
+ options.rsc_cmd = cmd_list_agents;
+ pcmk__str_update(&options.agent_spec, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-o", "--list-all-operations",
+ NULL)) {
+ options.rsc_cmd = cmd_list_all_ops;
+
+ } else if (pcmk__str_eq(option_name, "--list-ocf-alternatives",
+ pcmk__str_none)) {
+ options.rsc_cmd = cmd_list_alternatives;
+ pcmk__str_update(&options.agent_spec, optarg);
+
+ } else if (pcmk__str_eq(option_name, "--list-options", pcmk__str_none)) {
+ options.rsc_cmd = cmd_list_options;
+ return validate_opt_list(optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) {
+ options.rsc_cmd = cmd_list_instances;
+
+ } else if (pcmk__str_eq(option_name, "--list-ocf-providers",
+ pcmk__str_none)) {
+ options.rsc_cmd = cmd_list_providers;
+ pcmk__str_update(&options.agent_spec, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) {
+ options.rsc_cmd = cmd_list_resources;
+
+ } else if (pcmk__str_eq(option_name, "--list-standards", pcmk__str_none)) {
+ options.rsc_cmd = cmd_list_standards;
+
+ } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) {
+ options.rsc_cmd = cmd_locate;
+
+ } else if (pcmk__str_eq(option_name, "--show-metadata", pcmk__str_none)) {
+ options.rsc_cmd = cmd_metadata;
+ pcmk__str_update(&options.agent_spec, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) {
+ options.rsc_cmd = cmd_move;
+
+ } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) {
+ options.rsc_cmd = cmd_query_xml;
+
+ } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) {
+ options.rsc_cmd = cmd_query_xml_raw;
+
+ } else if (pcmk__str_any_of(option_name, "-R", "--refresh", NULL)) {
+ options.rsc_cmd = cmd_refresh;
+
+ } else if (pcmk__str_eq(option_name, "--restart", pcmk__str_none)) {
+ options.rsc_cmd = cmd_restart;
+
+ } else if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) {
+ options.rsc_cmd = cmd_set_param;
+ pcmk__str_update(&options.prop_name, optarg);
+
+ } else if (pcmk__str_any_of(option_name, "-S", "--set-property", NULL)) {
+ options.rsc_cmd = cmd_set_property;
+ pcmk__str_update(&options.prop_name, optarg);
+
+ } else if (pcmk__str_eq(option_name, "--wait", pcmk__str_none)) {
+ options.rsc_cmd = cmd_wait;
+
+ } else if (pcmk__str_any_of(option_name, "-Y", "--why", NULL)) {
+ options.rsc_cmd = cmd_why;
+ }
+
+ return TRUE;
+}
+
/* short option letters still available: eEJkKXyYZ */
static GOptionEntry query_entries[] = {
- { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb,
+ { "list", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"List all cluster resources with status",
NULL },
- { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb,
+ { "list-raw", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"List IDs of all instantiated resources (individual members\n"
INDENT "rather than groups etc.)",
NULL },
- { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb,
+ { "list-cts", 'c', G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, command_cb,
NULL,
NULL },
- { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb,
+ { "list-operations", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ command_cb,
"List active resource operations, optionally filtered by\n"
INDENT "--resource and/or --node",
NULL },
- { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, list_cb,
+ { "list-all-operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ command_cb,
"List all resource operations, optionally filtered by\n"
INDENT "--resource and/or --node",
NULL },
+ { "list-options", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
+ "List all available options of the given type\n"
+ INDENT "Allowed values:\n"
+ INDENT PCMK__VALUE_PRIMITIVE "(primitive resource meta-attributes), "
+ INDENT PCMK_VALUE_FENCING " (parameters common to all fencing resources)",
+ "TYPE" },
{ "list-standards", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- list_standards_cb,
+ command_cb,
"List supported standards",
NULL },
{ "list-ocf-providers", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- list_providers_cb,
+ command_cb,
"List all available OCF providers",
NULL },
{ "list-agents", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
- list_agents_cb,
+ command_cb,
"List all agents available for the named standard and/or provider",
"STD:PROV" },
{ "list-ocf-alternatives", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
- list_alternatives_cb,
+ command_cb,
"List all available providers for the named OCF agent",
"AGENT" },
- { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
- metadata_cb,
+ { "show-metadata", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, command_cb,
"Show the metadata for the named class:provider:agent",
"SPEC" },
- { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "query-xml", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Show XML configuration of resource (after any template expansion)",
NULL },
- { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "query-xml-raw", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Show XML configuration of resource (before any template expansion)",
NULL },
- { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, get_param_prop_cb,
+ { "get-parameter", 'g', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Display named parameter for resource (use instance attribute\n"
INDENT "unless --element, --meta, or --utilization is specified)",
"PARAM" },
- { "get-property", 'G', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, get_param_prop_cb,
+ { "get-property", 'G', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Display named property of resource ('class', 'type', or 'provider') "
"(requires --resource)",
"PROPERTY" },
- { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "locate", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Show node(s) currently running resource",
NULL },
- { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "constraints", 'a', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Display the location and colocation constraints that apply to a\n"
INDENT "resource, and if --recursive is specified, to the resources\n"
INDENT "directly or indirectly involved in those colocations.\n"
@@ -404,10 +539,10 @@ static GOptionEntry query_entries[] = {
INDENT "bundle instance, constraints for the collective resource\n"
INDENT "will be shown unless --force is given.",
NULL },
- { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "stack", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Equivalent to --constraints --recursive",
NULL },
- { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, why_cb,
+ { "why", 'Y', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Show why resources are not running, optionally filtered by\n"
INDENT "--resource and/or --node",
NULL },
@@ -417,7 +552,7 @@ static GOptionEntry query_entries[] = {
static GOptionEntry command_entries[] = {
{ "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ command_cb,
"Validate resource configuration by calling agent's validate-all\n"
INDENT "action. The configuration may be specified either by giving an\n"
INDENT "existing resource name with -r, or by specifying --class,\n"
@@ -425,7 +560,7 @@ static GOptionEntry command_entries[] = {
INDENT "--option arguments. An optional LEVEL argument can be given\n"
INDENT "to control the level of checking performed.",
"LEVEL" },
- { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
+ { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"If resource has any past failures, clear its history and fail\n"
INDENT "count. Optionally filtered by --resource, --node, --operation\n"
INDENT "and --interval (otherwise all). --operation and --interval\n"
@@ -435,23 +570,26 @@ static GOptionEntry command_entries[] = {
INDENT "resource, the clean-up applies to the whole collective resource\n"
INDENT "unless --force is given.",
NULL },
- { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
+ { "refresh", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Delete resource's history (including failures) so its current state\n"
INDENT "is rechecked. Optionally filtered by --resource and --node\n"
INDENT "(otherwise all). If the named resource is part of a group, or one\n"
INDENT "numbered instance of a clone or bundled resource, the refresh\n"
INDENT "applies to the whole collective resource unless --force is given.",
NULL },
- { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, set_delete_param_cb,
+ { "set-parameter", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Set named parameter for resource (requires -v). Use instance\n"
INDENT "attribute unless --element, --meta, or --utilization is "
"specified.",
"PARAM" },
- { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, set_delete_param_cb,
+ { "delete-parameter", 'd', G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Delete named parameter for resource. Use instance attribute\n"
INDENT "unless --element, --meta or, --utilization is specified.",
"PARAM" },
- { "set-property", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, set_prop_cb,
+ { "set-property", 'S', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
+ command_cb,
"Set named property of resource ('class', 'type', or 'provider') "
"(requires -r, -t, -v)",
"PROPERTY" },
@@ -460,7 +598,7 @@ static GOptionEntry command_entries[] = {
};
static GOptionEntry location_entries[] = {
- { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "move", 'M', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Create a constraint to move resource. If --node is specified,\n"
INDENT "the constraint will be to move to that node, otherwise it\n"
INDENT "will be to ban the current node. Unless --force is specified\n"
@@ -471,7 +609,7 @@ static GOptionEntry location_entries[] = {
INDENT "resource from running on its previous location until the\n"
INDENT "implicit constraint expires or is removed with --clear.",
NULL },
- { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "ban", 'B', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Create a constraint to keep resource off a node.\n"
INDENT "Optional: --node, --lifetime, --promoted.\n"
INDENT "NOTE: This will prevent the resource from running on the\n"
@@ -479,10 +617,10 @@ static GOptionEntry location_entries[] = {
INDENT "removed with --clear. If --node is not specified, it defaults\n"
INDENT "to the node currently running the resource for primitives\n"
INDENT "and groups, or the promoted instance of promotable clones with\n"
- INDENT "promoted-max=1 (all other situations result in an error as\n"
- INDENT "there is no sane default).",
+ INDENT PCMK_META_PROMOTED_MAX "=1 (all other situations result in an\n"
+ INDENT "error as there is no sane default).",
NULL },
- { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, flag_cb,
+ { "clear", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"Remove all constraints created by the --ban and/or --move\n"
INDENT "commands. Requires: --resource. Optional: --node, --promoted,\n"
INDENT "--expired. If --node is not specified, all constraints created\n"
@@ -492,7 +630,8 @@ static GOptionEntry location_entries[] = {
INDENT "node. If --expired is specified, only those constraints whose\n"
INDENT "lifetimes have expired will be removed.",
NULL },
- { "expired", 'e', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, expired_cb,
+ { "expired", 'e', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
+ &options.clear_expired,
"Modifies the --clear argument to remove constraints with\n"
INDENT "expired lifetimes.",
NULL },
@@ -516,20 +655,20 @@ static GOptionEntry location_entries[] = {
};
static GOptionEntry advanced_entries[] = {
- { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, delete_cb,
+ { "delete", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Delete a resource from the CIB. Required: -t",
NULL },
- { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, fail_cb,
+ { "fail", 'F', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Tell the cluster this resource has failed",
NULL },
- { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, restart_cb,
+ { "restart", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Tell the cluster to restart this resource and\n"
INDENT "anything that depends on it",
NULL },
- { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, wait_cb,
+ { "wait", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Wait until the cluster settles into a stable state",
NULL },
- { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, digests_cb,
+ { "digests", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Show parameter hashes that Pacemaker uses to detect\n"
INDENT "configuration changes (only accurate if there is resource\n"
INDENT "history on the specified node). Required: --resource, --node.\n"
@@ -538,32 +677,30 @@ static GOptionEntry advanced_entries[] = {
INDENT "changes).",
NULL },
{ "force-demote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ command_cb,
"(Advanced) Bypass the cluster and demote a resource on the local\n"
INDENT "node. Unless --force is specified, this will refuse to do so if\n"
INDENT "the cluster believes the resource is a clone instance already\n"
INDENT "running on the local node.",
NULL },
- { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ { "force-stop", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Bypass the cluster and stop a resource on the local node",
NULL },
- { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ { "force-start", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
"(Advanced) Bypass the cluster and start a resource on the local\n"
INDENT "node. Unless --force is specified, this will refuse to do so if\n"
INDENT "the cluster believes the resource is a clone instance already\n"
INDENT "running on the local node.",
NULL },
{ "force-promote", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ command_cb,
"(Advanced) Bypass the cluster and promote a resource on the local\n"
INDENT "node. Unless --force is specified, this will refuse to do so if\n"
INDENT "the cluster believes the resource is a clone instance already\n"
INDENT "running on the local node.",
NULL },
{ "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
+ command_cb,
"(Advanced) Bypass the cluster and check the state of a resource on\n"
INDENT "the local node. An optional LEVEL argument can be given\n"
INDENT "to control the level of checking performed.",
@@ -603,15 +740,16 @@ static GOptionEntry addl_entries[] = {
{ "interval", 'I', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.interval_spec,
"Interval of operation to clear (default 0) (with -C -r -n)",
"N" },
- { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, class_cb,
+ { "class", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb,
"The standard the resource agent conforms to (for example, ocf).\n"
INDENT "Use with --agent, --provider, --option, and --validate.",
"CLASS" },
- { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
+ { "agent", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, cmdline_config_cb,
"The agent to use (for example, IPaddr). Use with --class,\n"
INDENT "--provider, --option, and --validate.",
"AGENT" },
- { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, agent_provider_cb,
+ { "provider", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
+ cmdline_config_cb,
"The vendor that supplies the resource agent (for example,\n"
INDENT "heartbeat). Use with --class, --agent, --option, and --validate.",
"PROVIDER" },
@@ -630,6 +768,10 @@ static GOptionEntry addl_entries[] = {
"(Advanced) Abort if command does not finish in this time (with\n"
INDENT "--restart, --wait, --force-*)",
"N" },
+ { "all", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.all,
+ "List all options, including advanced and deprecated (with\n"
+ INDENT "--list-options)",
+ NULL },
{ "force", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &options.force,
"Force the action to be performed. See help for individual commands for\n"
INDENT "additional behavior.",
@@ -644,136 +786,33 @@ static GOptionEntry addl_entries[] = {
{ NULL }
};
-static void
-reset_options(void) {
- options.require_crmd = FALSE;
- options.require_node = FALSE;
-
- options.require_cib = TRUE;
- options.require_scheduler = TRUE;
- options.require_resource = TRUE;
-
- options.find_flags = 0;
-}
-
-gboolean
-agent_provider_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.cmdline_config = TRUE;
- options.require_resource = FALSE;
-
- if (pcmk__str_eq(option_name, "--provider", pcmk__str_casei)) {
- pcmk__str_update(&options.v_provider, optarg);
- } else {
- pcmk__str_update(&options.v_agent, optarg);
- }
-
- return TRUE;
-}
-
gboolean
attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (pcmk__str_any_of(option_name, "-m", "--meta", NULL)) {
- options.attr_set_type = XML_TAG_META_SETS;
+ options.attr_set_type = PCMK_XE_META_ATTRIBUTES;
} else if (pcmk__str_any_of(option_name, "-z", "--utilization", NULL)) {
- options.attr_set_type = XML_TAG_UTILIZATION;
- } else if (pcmk__str_eq(option_name, "--element", pcmk__str_casei)) {
+ options.attr_set_type = PCMK_XE_UTILIZATION;
+ } else if (pcmk__str_eq(option_name, "--element", pcmk__str_none)) {
options.attr_set_type = ATTR_SET_ELEMENT;
}
return TRUE;
}
gboolean
-class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- pcmk__str_update(&options.v_class, optarg);
- options.cmdline_config = TRUE;
- options.require_resource = FALSE;
- return TRUE;
-}
-
-gboolean
-cleanup_refresh_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- if (pcmk__str_any_of(option_name, "-C", "--cleanup", NULL)) {
- SET_COMMAND(cmd_cleanup);
- } else {
- SET_COMMAND(cmd_refresh);
- }
-
- options.require_resource = FALSE;
- if (getenv("CIB_file") == NULL) {
- options.require_crmd = TRUE;
- }
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
- return TRUE;
-}
-
-gboolean
-delete_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- SET_COMMAND(cmd_delete);
- options.require_scheduler = FALSE;
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- return TRUE;
-}
-
-gboolean
-expired_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.clear_expired = TRUE;
- options.require_resource = FALSE;
- return TRUE;
-}
-
-static void
-get_agent_spec(const gchar *optarg)
-{
- options.require_cib = FALSE;
- options.require_scheduler = FALSE;
- options.require_resource = FALSE;
- pcmk__str_update(&options.agent_spec, optarg);
-}
-
-gboolean
-list_agents_cb(const gchar *option_name, const gchar *optarg, gpointer data,
- GError **error)
-{
- SET_COMMAND(cmd_list_agents);
- get_agent_spec(optarg);
- return TRUE;
-}
-
-gboolean
-list_providers_cb(const gchar *option_name, const gchar *optarg, gpointer data,
+cmdline_config_cb(const gchar *option_name, const gchar *optarg, gpointer data,
GError **error)
{
- SET_COMMAND(cmd_list_providers);
- get_agent_spec(optarg);
- return TRUE;
-}
+ options.cmdline_config = true;
-gboolean
-list_standards_cb(const gchar *option_name, const gchar *optarg, gpointer data,
- GError **error)
-{
- SET_COMMAND(cmd_list_standards);
- options.require_cib = FALSE;
- options.require_scheduler = FALSE;
- options.require_resource = FALSE;
- return TRUE;
-}
+ if (pcmk__str_eq(option_name, "--class", pcmk__str_none)) {
+ pcmk__str_update(&options.v_class, optarg);
-gboolean
-list_alternatives_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error)
-{
- SET_COMMAND(cmd_list_alternatives);
- get_agent_spec(optarg);
- return TRUE;
-}
+ } else if (pcmk__str_eq(option_name, "--provider", pcmk__str_none)) {
+ pcmk__str_update(&options.v_provider, optarg);
-gboolean
-metadata_cb(const gchar *option_name, const gchar *optarg, gpointer data,
- GError **error)
-{
- SET_COMMAND(cmd_metadata);
- get_agent_spec(optarg);
+ } else { // --agent
+ pcmk__str_update(&options.v_agent, optarg);
+ }
return TRUE;
}
@@ -795,173 +834,16 @@ option_cb(const gchar *option_name, const gchar *optarg, gpointer data,
}
gboolean
-fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- SET_COMMAND(cmd_fail);
- options.require_crmd = TRUE;
- options.require_node = TRUE;
- return TRUE;
-}
-
-gboolean
-flag_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- if (pcmk__str_any_of(option_name, "-U", "--clear", NULL)) {
- SET_COMMAND(cmd_clear);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
- } else if (pcmk__str_any_of(option_name, "-B", "--ban", NULL)) {
- SET_COMMAND(cmd_ban);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
- } else if (pcmk__str_any_of(option_name, "-M", "--move", NULL)) {
- SET_COMMAND(cmd_move);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
- } else if (pcmk__str_any_of(option_name, "-q", "--query-xml", NULL)) {
- SET_COMMAND(cmd_query_xml);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- } else if (pcmk__str_any_of(option_name, "-w", "--query-xml-raw", NULL)) {
- SET_COMMAND(cmd_query_raw_xml);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- } else if (pcmk__str_any_of(option_name, "-W", "--locate", NULL)) {
- SET_COMMAND(cmd_locate);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
-
- } else if (pcmk__str_any_of(option_name, "-a", "--constraints", NULL)) {
- SET_COMMAND(cmd_colocations);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
-
- } else if (pcmk__str_any_of(option_name, "-A", "--stack", NULL)) {
- SET_COMMAND(cmd_colocations);
- options.find_flags = pcmk_rsc_match_history
- |pcmk_rsc_match_anon_basename;
- options.recursive = TRUE;
- }
-
- return TRUE;
-}
-
-gboolean
-get_param_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- if (pcmk__str_any_of(option_name, "-g", "--get-parameter", NULL)) {
- SET_COMMAND(cmd_get_param);
- } else {
- SET_COMMAND(cmd_get_property);
- }
-
- pcmk__str_update(&options.prop_name, optarg);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- return TRUE;
-}
-
-gboolean
-list_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- if (pcmk__str_any_of(option_name, "-c", "--list-cts", NULL)) {
- SET_COMMAND(cmd_cts);
- } else if (pcmk__str_any_of(option_name, "-L", "--list", NULL)) {
- SET_COMMAND(cmd_list_resources);
- } else if (pcmk__str_any_of(option_name, "-l", "--list-raw", NULL)) {
- SET_COMMAND(cmd_list_instances);
- } else if (pcmk__str_any_of(option_name, "-O", "--list-operations", NULL)) {
- SET_COMMAND(cmd_list_active_ops);
- } else {
- SET_COMMAND(cmd_list_all_ops);
- }
-
- options.require_resource = FALSE;
- return TRUE;
-}
-
-gboolean
-set_delete_param_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- if (pcmk__str_any_of(option_name, "-p", "--set-parameter", NULL)) {
- SET_COMMAND(cmd_set_param);
- } else {
- SET_COMMAND(cmd_delete_param);
- }
-
- pcmk__str_update(&options.prop_name, optarg);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- return TRUE;
-}
-
-gboolean
-set_prop_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- SET_COMMAND(cmd_set_property);
- options.require_scheduler = FALSE;
- pcmk__str_update(&options.prop_name, optarg);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_basename;
- return TRUE;
-}
-
-gboolean
timeout_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.timeout_ms = crm_get_msec(optarg);
- return TRUE;
-}
-
-gboolean
-validate_or_force_cb(const gchar *option_name, const gchar *optarg,
- gpointer data, GError **error)
-{
- SET_COMMAND(cmd_execute_agent);
- if (options.operation) {
- g_free(options.operation);
- }
- options.operation = g_strdup(option_name + 2); // skip "--"
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
- if (options.override_params == NULL) {
- options.override_params = pcmk__strkey_table(free, free);
- }
-
- if (optarg != NULL) {
- if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) {
- g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
- _("Invalid check level setting: %s"), optarg);
- return FALSE;
- }
- }
-
- return TRUE;
-}
+ long long timeout_ms = crm_get_msec(optarg);
-gboolean
-restart_cb(const gchar *option_name, const gchar *optarg, gpointer data,
- GError **error)
-{
- SET_COMMAND(cmd_restart);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
- return TRUE;
-}
-
-gboolean
-digests_cb(const gchar *option_name, const gchar *optarg, gpointer data,
- GError **error)
-{
- SET_COMMAND(cmd_digests);
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
- if (options.override_params == NULL) {
- options.override_params = pcmk__strkey_table(free, free);
+ if (timeout_ms < 0) {
+ // @COMPAT When we can break backward compatibilty, return FALSE
+ crm_warn("Ignoring invalid timeout '%s'", optarg);
+ options.timeout_ms = 0U;
+ } else {
+ options.timeout_ms = (guint) QB_MIN(timeout_ms, UINT_MAX);
}
- options.require_node = TRUE;
- options.require_scheduler = TRUE;
- return TRUE;
-}
-
-gboolean
-wait_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- SET_COMMAND(cmd_wait);
- options.require_resource = FALSE;
- options.require_scheduler = FALSE;
- return TRUE;
-}
-
-gboolean
-why_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- SET_COMMAND(cmd_why);
- options.require_resource = FALSE;
- options.find_flags = pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
return TRUE;
}
@@ -979,8 +861,8 @@ ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc,
if (nactive == 1) {
rc = cli_resource_ban(out, options.rsc_id, current->details->uname, move_lifetime,
- cib_conn, options.cib_options, options.promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ cib_conn, cib_sync_call,
+ options.promoted_role_only, PCMK_ROLE_PROMOTED);
} else if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
int count = 0;
@@ -993,14 +875,15 @@ ban_or_move(pcmk__output_t *out, pcmk_resource_t *rsc,
if (child_role == pcmk_role_promoted) {
count++;
- current = pe__current_node(child);
+ current = pcmk__current_node(child);
}
}
if(count == 1 && current) {
rc = cli_resource_ban(out, options.rsc_id, current->details->uname, move_lifetime,
- cib_conn, options.cib_options, options.promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ cib_conn, cib_sync_call,
+ options.promoted_role_only,
+ PCMK_ROLE_PROMOTED);
} else {
rc = EINVAL;
@@ -1066,12 +949,12 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy)
if (options.clear_expired) {
rc = cli_resource_clear_all_expired(scheduler->input, cib_conn,
- options.cib_options, options.rsc_id,
+ cib_sync_call, options.rsc_id,
options.host_uname,
options.promoted_role_only);
} else if (options.host_uname) {
- dest = pe_find_node(scheduler->nodes, options.host_uname);
+ dest = pcmk_find_node(scheduler, options.host_uname);
if (dest == NULL) {
rc = pcmk_rc_node_unknown;
if (!out->is_quiet(out)) {
@@ -1080,11 +963,11 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy)
return rc;
}
rc = cli_resource_clear(options.rsc_id, dest->details->uname, NULL,
- cib_conn, options.cib_options, TRUE, options.force);
+ cib_conn, cib_sync_call, true, options.force);
} else {
rc = cli_resource_clear(options.rsc_id, NULL, scheduler->nodes,
- cib_conn, options.cib_options, TRUE, options.force);
+ cib_conn, cib_sync_call, true, options.force);
}
if (!out->is_quiet(out)) {
@@ -1119,35 +1002,12 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy)
}
static int
-delete(void)
-{
- int rc = pcmk_rc_ok;
- xmlNode *msg_data = NULL;
-
- if (options.rsc_type == NULL) {
- rc = ENXIO;
- g_set_error(&error, PCMK__RC_ERROR, rc,
- _("You need to specify a resource type with -t"));
- return rc;
- }
-
- msg_data = create_xml_node(NULL, options.rsc_type);
- crm_xml_add(msg_data, XML_ATTR_ID, options.rsc_id);
-
- rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_RESOURCES, msg_data,
- options.cib_options);
- rc = pcmk_legacy2rc(rc);
- free_xml(msg_data);
- return rc;
-}
-
-static int
initialize_scheduler_data(xmlNodePtr *cib_xml_copy)
{
int rc = pcmk_rc_ok;
if (options.xml_file != NULL) {
- *cib_xml_copy = filename2xml(options.xml_file);
+ *cib_xml_copy = pcmk__xml_read(options.xml_file);
if (*cib_xml_copy == NULL) {
rc = pcmk_rc_cib_corrupt;
}
@@ -1161,7 +1021,7 @@ initialize_scheduler_data(xmlNodePtr *cib_xml_copy)
if (scheduler == NULL) {
rc = ENOMEM;
} else {
- pe__set_working_set_flags(scheduler,
+ pcmk__set_scheduler_flags(scheduler,
pcmk_sched_no_counts
|pcmk_sched_no_compat);
scheduler->priv = out;
@@ -1179,6 +1039,26 @@ initialize_scheduler_data(xmlNodePtr *cib_xml_copy)
return pcmk_rc_ok;
}
+static void
+list_options(void)
+{
+ switch (options.opt_list) {
+ case pcmk__opt_fencing:
+ exit_code = pcmk_rc2exitc(pcmk__list_fencing_params(out,
+ options.all));
+ break;
+ case pcmk__opt_primitive:
+ exit_code = pcmk_rc2exitc(pcmk__list_primitive_meta(out,
+ options.all));
+ break;
+ default:
+ exit_code = CRM_EX_SOFTWARE;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "BUG: Invalid option list type");
+ break;
+ }
+}
+
static int
refresh(pcmk__output_t *out)
{
@@ -1187,10 +1067,10 @@ refresh(pcmk__output_t *out)
int attr_options = pcmk__node_attr_none;
if (options.host_uname) {
- pcmk_node_t *node = pe_find_node(scheduler->nodes, options.host_uname);
+ pcmk_node_t *node = pcmk_find_node(scheduler, options.host_uname);
- if (pe__is_guest_or_remote_node(node)) {
- node = pe__current_node(node->details->remote_rsc);
+ if (pcmk__is_pacemaker_remote_node(node)) {
+ node = pcmk__current_node(node->details->remote_rsc);
if (node == NULL) {
rc = ENXIO;
g_set_error(&error, PCMK__RC_ERROR, rc,
@@ -1268,12 +1148,12 @@ set_property(void)
CRM_LOG_ASSERT(options.prop_name != NULL);
- msg_data = create_xml_node(NULL, options.rsc_type);
- crm_xml_add(msg_data, XML_ATTR_ID, options.rsc_id);
+ msg_data = pcmk__xe_create(NULL, options.rsc_type);
+ crm_xml_add(msg_data, PCMK_XA_ID, options.rsc_id);
crm_xml_add(msg_data, options.prop_name, options.prop_value);
- rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_RESOURCES, msg_data,
- options.cib_options);
+ rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_RESOURCES, msg_data,
+ cib_sync_call);
rc = pcmk_legacy2rc(rc);
free_xml(msg_data);
@@ -1308,7 +1188,7 @@ show_metadata(pcmk__output_t *out, const char *agent_spec)
rc = pcmk_legacy2rc(rc);
if (metadata) {
- out->output_xml(out, "metadata", metadata);
+ out->output_xml(out, PCMK_XE_METADATA, metadata);
free(metadata);
} else {
/* We were given a validly formatted spec, but it doesn't necessarily
@@ -1366,16 +1246,209 @@ validate_cmdline_config(void)
options.v_agent ? options.v_agent : "");
}
- if (error != NULL) {
- return;
+ if ((error == NULL) && (options.cmdline_params == NULL)) {
+ options.cmdline_params = pcmk__strkey_table(free, free);
}
+}
- if (options.cmdline_params == NULL) {
- options.cmdline_params = pcmk__strkey_table(free, free);
+/*!
+ * \internal
+ * \brief Get the <tt>enum pe_find</tt> flags for a given command
+ *
+ * \return <tt>enum pe_find</tt> flag group appropriate for \c options.rsc_cmd.
+ */
+static uint32_t
+get_find_flags(void)
+{
+ switch (options.rsc_cmd) {
+ case cmd_ban:
+ case cmd_cleanup:
+ case cmd_clear:
+ case cmd_colocations:
+ case cmd_digests:
+ case cmd_execute_agent:
+ case cmd_locate:
+ case cmd_move:
+ case cmd_refresh:
+ case cmd_restart:
+ case cmd_why:
+ return pcmk_rsc_match_history|pcmk_rsc_match_anon_basename;
+
+ // @COMPAT See note in is_scheduler_required()
+ case cmd_delete:
+ case cmd_delete_param:
+ case cmd_get_param:
+ case cmd_get_property:
+ case cmd_query_xml_raw:
+ case cmd_query_xml:
+ case cmd_set_param:
+ case cmd_set_property:
+ return pcmk_rsc_match_history|pcmk_rsc_match_basename;
+
+ default:
+ return 0;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether a node argument is required
+ *
+ * \return \c true if a \c --node argument is required, or \c false otherwise
+ */
+static bool
+is_node_required(void)
+{
+ switch (options.rsc_cmd) {
+ case cmd_digests:
+ case cmd_fail:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether a resource argument is required
+ *
+ * \return \c true if a \c --resource argument is required, or \c false
+ * otherwise
+ */
+static bool
+is_resource_required(void)
+{
+ if (options.cmdline_config) {
+ return false;
+ }
+
+ switch (options.rsc_cmd) {
+ case cmd_clear:
+ return !options.clear_expired;
+
+ case cmd_cleanup:
+ case cmd_cts:
+ case cmd_list_active_ops:
+ case cmd_list_agents:
+ case cmd_list_all_ops:
+ case cmd_list_alternatives:
+ case cmd_list_instances:
+ case cmd_list_options:
+ case cmd_list_providers:
+ case cmd_list_resources:
+ case cmd_list_standards:
+ case cmd_metadata:
+ case cmd_refresh:
+ case cmd_wait:
+ case cmd_why:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether a CIB connection is required
+ *
+ * \return \c true if a CIB connection is required, or \c false otherwise
+ */
+static bool
+is_cib_required(void)
+{
+ if (options.cmdline_config) {
+ return false;
+ }
+
+ switch (options.rsc_cmd) {
+ case cmd_list_agents:
+ case cmd_list_alternatives:
+ case cmd_list_options:
+ case cmd_list_providers:
+ case cmd_list_standards:
+ case cmd_metadata:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether a controller IPC connection is required
+ *
+ * \return \c true if a controller connection is required, or \c false otherwise
+ */
+static bool
+is_controller_required(void)
+{
+ switch (options.rsc_cmd) {
+ case cmd_cleanup:
+ case cmd_refresh:
+ return getenv("CIB_file") == NULL;
+
+ case cmd_fail:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether a scheduler IPC connection is required
+ *
+ * \return \c true if a scheduler connection is required, or \c false otherwise
+ */
+static bool
+is_scheduler_required(void)
+{
+ if (options.cmdline_config) {
+ return false;
+ }
+
+ /* @COMPAT cmd_delete does not actually need the scheduler and should not
+ * set find_flags. However, crm_resource --delete currently throws a
+ * "resource not found" error if the resource doesn't exist. This is
+ * incorrect behavior (deleting a nonexistent resource should be considered
+ * success); however, we shouldn't change it until 3.0.0.
+ */
+ switch (options.rsc_cmd) {
+ case cmd_list_agents:
+ case cmd_list_alternatives:
+ case cmd_list_options:
+ case cmd_list_providers:
+ case cmd_list_standards:
+ case cmd_metadata:
+ case cmd_wait:
+ return false;
+ default:
+ return true;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check whether the chosen command accepts clone instances
+ *
+ * \return \c true if \p options.rsc_cmd accepts or ignores clone instances, or
+ * \c false otherwise
+ */
+static bool
+accept_clone_instance(void)
+{
+ // @COMPAT At 3.0.0, add cmd_delete; for now, don't throw error
+ switch (options.rsc_cmd) {
+ case cmd_ban:
+ case cmd_clear:
+ case cmd_move:
+ case cmd_restart:
+ return false;
+ default:
+ return true;
}
- options.require_resource = FALSE;
- options.require_scheduler = FALSE;
- options.require_cib = FALSE;
}
static GOptionContext *
@@ -1407,14 +1480,14 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
"location:\n\n"
"\t# crm_resource --resource myResource --clear\n\n"
"Stop 'myResource' (and anything that depends on it):\n\n"
- "\t# crm_resource --resource myResource --set-parameter target-role "
- "--meta --parameter-value Stopped\n\n"
+ "\t# crm_resource --resource myResource --set-parameter "
+ PCMK_META_TARGET_ROLE "--meta --parameter-value Stopped\n\n"
"Tell the cluster not to manage 'myResource' (the cluster will not "
"attempt to start or stop the\n"
"resource under any circumstances; useful when performing maintenance "
"tasks on a resource):\n\n"
- "\t# crm_resource --resource myResource --set-parameter is-managed "
- "--meta --parameter-value false\n\n"
+ "\t# crm_resource --resource myResource --set-parameter "
+ PCMK_META_IS_MANAGED "--meta --parameter-value false\n\n"
"Erase the operation history of 'myResource' on 'aNode' (the cluster "
"will 'forget' the existing\n"
"resource state, including any errors, and attempt to recover the"
@@ -1449,6 +1522,7 @@ main(int argc, char **argv)
xmlNode *cib_xml_copy = NULL;
pcmk_resource_t *rsc = NULL;
pcmk_node_t *node = NULL;
+ uint32_t find_flags = 0;
int rc = pcmk_rc_ok;
GOptionGroup *output_group = NULL;
@@ -1492,12 +1566,6 @@ main(int argc, char **argv)
* Validate option combinations
*/
- // If the user didn't explicitly specify a command, list resources
- if (options.rsc_cmd == cmd_none) {
- options.rsc_cmd = cmd_list_resources;
- options.require_resource = FALSE;
- }
-
// --expired without --clear/-U doesn't make sense
if (options.clear_expired && (options.rsc_cmd != cmd_clear)) {
exit_code = CRM_EX_USAGE;
@@ -1508,8 +1576,8 @@ main(int argc, char **argv)
if ((options.remainder != NULL) && (options.override_params != NULL)) {
// Commands that use positional arguments will create override_params
for (gchar **s = options.remainder; *s; s++) {
- char *name = calloc(1, strlen(*s));
- char *value = calloc(1, strlen(*s));
+ char *name = pcmk__assert_alloc(1, strlen(*s));
+ char *value = pcmk__assert_alloc(1, strlen(*s));
int rc = sscanf(*s, "%[^=]=%s", name, value);
if (rc == 2) {
@@ -1541,7 +1609,7 @@ main(int argc, char **argv)
/* Add 1 for the strv[0] string below, and add another 1 for the NULL
* at the end of the array so g_strjoinv knows when to stop.
*/
- strv = calloc(len+2, sizeof(char *));
+ strv = pcmk__assert_alloc(len+2, sizeof(char *));
strv[0] = strdup("non-option ARGV-elements:\n");
for (gchar **s = options.remainder; *s; s++) {
@@ -1566,28 +1634,30 @@ main(int argc, char **argv)
}
if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
- /* Kind of a hack to display XML lists using a real tag instead of <list>. This just
- * saves from having to write custom messages to build the lists around all these things
- */
switch (options.rsc_cmd) {
- case cmd_execute_agent:
- case cmd_list_resources:
- case cmd_query_xml:
- case cmd_query_raw_xml:
- case cmd_list_active_ops:
- case cmd_list_all_ops:
- case cmd_colocations:
- pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname());
+ /* These are the only commands that have historically used the <list>
+ * elements in their XML schema. For all others, use the simple list
+ * argument.
+ */
+ case cmd_get_param:
+ case cmd_get_property:
+ case cmd_list_instances:
+ case cmd_list_standards:
+ pcmk__output_enable_list_element(out);
break;
default:
- pcmk__force_args(context, &error, "%s --xml-substitute", g_get_prgname());
break;
}
+
} else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) {
- if ((options.rsc_cmd == cmd_colocations) ||
- options.rsc_cmd == cmd_list_resources) {
- pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname());
+ switch (options.rsc_cmd) {
+ case cmd_colocations:
+ case cmd_list_resources:
+ pcmk__output_text_set_fancy(out, true);
+ break;
+ default:
+ break;
}
}
@@ -1612,13 +1682,13 @@ main(int argc, char **argv)
options.cmdline_params = NULL;
}
- if (options.require_resource && (options.rsc_id == NULL)) {
+ if (is_resource_required() && (options.rsc_id == NULL)) {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
_("Must supply a resource id with -r"));
goto done;
}
- if (options.require_node && (options.host_uname == NULL)) {
+ if (is_node_required() && (options.host_uname == NULL)) {
exit_code = CRM_EX_USAGE;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
_("Must supply a node name with -N"));
@@ -1629,12 +1699,8 @@ main(int argc, char **argv)
* Set up necessary connections
*/
- if (options.find_flags && options.rsc_id) {
- options.require_scheduler = TRUE;
- }
-
// Establish a connection to the CIB if needed
- if (options.require_cib) {
+ if (is_cib_required()) {
cib_conn = cib_new();
if ((cib_conn == NULL) || (cib_conn->cmds == NULL)) {
exit_code = CRM_EX_DISCONNECT;
@@ -1653,7 +1719,7 @@ main(int argc, char **argv)
}
// Populate scheduler data from XML file if specified or CIB query otherwise
- if (options.require_scheduler) {
+ if (is_scheduler_required()) {
rc = initialize_scheduler_data(&cib_xml_copy);
if (rc != pcmk_rc_ok) {
exit_code = pcmk_rc2exitc(rc);
@@ -1661,10 +1727,12 @@ main(int argc, char **argv)
}
}
+ find_flags = get_find_flags();
+
// If command requires that resource exist if specified, find it
- if (options.find_flags && options.rsc_id) {
+ if ((find_flags != 0) && (options.rsc_id != NULL)) {
rsc = pe_find_resource_with_flags(scheduler->resources, options.rsc_id,
- options.find_flags);
+ find_flags);
if (rsc == NULL) {
exit_code = CRM_EX_NOSUCH;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
@@ -1675,9 +1743,9 @@ main(int argc, char **argv)
/* The --ban, --clear, --move, and --restart commands do not work with
* instances of clone resourcs.
*/
- if (strchr(options.rsc_id, ':') != NULL && pe_rsc_is_clone(rsc->parent) &&
- (options.rsc_cmd == cmd_ban || options.rsc_cmd == cmd_clear ||
- options.rsc_cmd == cmd_move || options.rsc_cmd == cmd_restart)) {
+ if (pcmk__is_clone(rsc->parent) && (strchr(options.rsc_id, ':') != NULL)
+ && !accept_clone_instance()) {
+
exit_code = CRM_EX_INVALID_PARAM;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
_("Cannot operate on clone resource instance '%s'"), options.rsc_id);
@@ -1687,7 +1755,7 @@ main(int argc, char **argv)
// If user supplied a node name, check whether it exists
if ((options.host_uname != NULL) && (scheduler != NULL)) {
- node = pe_find_node(scheduler->nodes, options.host_uname);
+ node = pcmk_find_node(scheduler, options.host_uname);
if (node == NULL) {
exit_code = CRM_EX_NOSUCH;
@@ -1698,7 +1766,7 @@ main(int argc, char **argv)
}
// Establish a connection to the controller if needed
- if (options.require_crmd) {
+ if (is_controller_required()) {
rc = pcmk_new_ipc_api(&controld_api, pcmk_ipc_controld);
if (rc != pcmk_rc_ok) {
exit_code = pcmk_rc2exitc(rc);
@@ -1725,10 +1793,11 @@ main(int argc, char **argv)
switch (options.rsc_cmd) {
case cmd_list_resources: {
GList *all = NULL;
+ uint32_t show_opts = pcmk_show_inactive_rscs | pcmk_show_rsc_only | pcmk_show_pending;
+
all = g_list_prepend(all, (gpointer) "*");
rc = out->message(out, "resource-list", scheduler,
- pcmk_show_inactive_rscs | pcmk_show_rsc_only | pcmk_show_pending,
- true, all, all, false);
+ show_opts, true, all, all, false);
g_list_free(all);
if (rc == pcmk_rc_no_output) {
@@ -1746,6 +1815,10 @@ main(int argc, char **argv)
break;
+ case cmd_list_options:
+ list_options();
+ break;
+
case cmd_list_alternatives:
rc = pcmk__list_alternatives(out, options.agent_spec);
break;
@@ -1774,7 +1847,7 @@ main(int argc, char **argv)
*/
rc = cli_resource_restart(out, rsc, node, options.move_lifetime,
options.timeout_ms, cib_conn,
- options.cib_options, options.promoted_role_only,
+ cib_sync_call, options.promoted_role_only,
options.force);
break;
@@ -1798,7 +1871,7 @@ main(int argc, char **argv)
goto done;
case cmd_digests:
- node = pe_find_node(scheduler->nodes, options.host_uname);
+ node = pcmk_find_node(scheduler, options.host_uname);
if (node == NULL) {
rc = pcmk_rc_node_unknown;
} else {
@@ -1850,7 +1923,7 @@ main(int argc, char **argv)
rc = cli_resource_print(rsc, scheduler, true);
break;
- case cmd_query_raw_xml:
+ case cmd_query_xml_raw:
rc = cli_resource_print(rsc, scheduler, false);
break;
@@ -1873,7 +1946,7 @@ main(int argc, char **argv)
} else {
rc = cli_resource_move(rsc, options.rsc_id, options.host_uname,
options.move_lifetime, cib_conn,
- options.cib_options, scheduler,
+ cib_sync_call, scheduler,
options.promoted_role_only,
options.force);
}
@@ -1893,9 +1966,8 @@ main(int argc, char **argv)
} else {
rc = cli_resource_ban(out, options.rsc_id, node->details->uname,
options.move_lifetime, cib_conn,
- options.cib_options,
- options.promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ cib_sync_call, options.promoted_role_only,
+ PCMK_ROLE_PROMOTED);
}
if (rc == EINVAL) {
@@ -1933,15 +2005,17 @@ main(int argc, char **argv)
crm_debug("Looking up %s in %s", options.prop_name, rsc->id);
- if (pcmk__str_eq(options.attr_set_type, XML_TAG_ATTR_SETS, pcmk__str_none)) {
+ if (pcmk__str_eq(options.attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES,
+ pcmk__str_none)) {
params = pe_rsc_params(rsc, current, scheduler);
free_params = false;
value = g_hash_table_lookup(params, options.prop_name);
- } else if (pcmk__str_eq(options.attr_set_type, XML_TAG_META_SETS, pcmk__str_none)) {
+ } else if (pcmk__str_eq(options.attr_set_type,
+ PCMK_XE_META_ATTRIBUTES, pcmk__str_none)) {
params = pcmk__strkey_table(free, free);
- get_meta_attributes(params, rsc, current, scheduler);
+ get_meta_attributes(params, rsc, NULL, scheduler);
value = g_hash_table_lookup(params, options.prop_name);
@@ -1951,9 +2025,14 @@ main(int argc, char **argv)
free_params = false;
} else {
+ pe_rule_eval_data_t rule_data = {
+ .now = scheduler->now,
+ };
+
params = pcmk__strkey_table(free, free);
- pe__unpack_dataset_nvpairs(rsc->xml, XML_TAG_UTILIZATION, NULL, params,
- NULL, FALSE, scheduler);
+ pe__unpack_dataset_nvpairs(rsc->xml, PCMK_XE_UTILIZATION,
+ &rule_data, params, NULL, FALSE,
+ scheduler);
value = g_hash_table_lookup(params, options.prop_name);
}
@@ -1982,7 +2061,6 @@ main(int argc, char **argv)
options.prop_name,
options.prop_value,
options.recursive, cib_conn,
- options.cib_options,
options.force);
break;
@@ -1993,8 +2071,7 @@ main(int argc, char **argv)
options.attr_set_type,
options.prop_id,
options.prop_name, cib_conn,
- options.cib_options,
- options.force);
+ cib_sync_call, options.force);
break;
case cmd_cleanup:
@@ -2019,7 +2096,25 @@ main(int argc, char **argv)
break;
case cmd_delete:
- rc = delete();
+ /* rsc_id was already checked for NULL much earlier when validating
+ * command line arguments.
+ */
+ if (options.rsc_type == NULL) {
+ // @COMPAT @TODO change this to exit_code = CRM_EX_USAGE
+ rc = ENXIO;
+ g_set_error(&error, PCMK__RC_ERROR, rc,
+ _("You need to specify a resource type with -t"));
+ } else {
+ rc = pcmk__resource_delete(cib_conn, cib_sync_call,
+ options.rsc_id, options.rsc_type);
+
+ if (rc != pcmk_rc_ok) {
+ g_set_error(&error, PCMK__RC_ERROR, rc,
+ _("Could not delete resource %s: %s"),
+ options.rsc_id, pcmk_rc_str(rc));
+ }
+ }
+
break;
default:
diff --git a/tools/crm_resource.h b/tools/crm_resource.h
index dc86572..de85a5b 100644
--- a/tools/crm_resource.h
+++ b/tools/crm_resource.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,6 @@
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/services.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
@@ -22,7 +21,7 @@
#include <crm/common/scheduler_internal.h>
#include <crm/cib.h>
-#include <crm/common/attrd_internal.h>
+#include <crm/common/attrs_internal.h>
#include <crm/pengine/rules.h>
#include <crm/pengine/status.h>
#include <crm/pengine/internal.h>
@@ -35,6 +34,16 @@ typedef struct node_info_s {
bool promoted;
} node_info_t;
+typedef struct {
+ char *attr_set_type;
+ char *attr_set_id;
+ char *attr_name;
+ char *attr_value;
+ char *given_rsc_id;
+ char *found_attr_id;
+ pcmk_resource_t *rsc;
+} attr_update_data_t;
+
enum resource_check_flags {
rsc_remain_stopped = (1 << 0),
rsc_unpromotable = (1 << 1),
@@ -89,7 +98,7 @@ int cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name,
pcmk_scheduler_t *scheduler);
int cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
const pcmk_node_t *node, const char *move_lifetime,
- int timeout_ms, cib_t *cib, int cib_options,
+ guint timeout_ms, cib_t *cib, int cib_options,
gboolean promoted_role_only, gboolean force);
int cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
const char *host_name, const char *move_lifetime,
@@ -99,12 +108,13 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc
const char *rsc_class, const char *rsc_prov,
const char *rsc_type, const char *rsc_action,
GHashTable *params, GHashTable *override_hash,
- int timeout_ms, int resource_verbose,
+ guint timeout_ms,
+ int resource_verbose,
gboolean force, int check_level);
crm_exit_t cli_resource_execute(pcmk_resource_t *rsc,
const char *requested_name,
const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t *cib,
+ guint timeout_ms, cib_t *cib,
pcmk_scheduler_t *scheduler,
int resource_verbose, gboolean force, int check_level);
@@ -113,7 +123,7 @@ int cli_resource_update_attribute(pcmk_resource_t *rsc,
const char *attr_set, const char *attr_set_type,
const char *attr_id, const char *attr_name,
const char *attr_value, gboolean recursive,
- cib_t *cib, int cib_options, gboolean force);
+ cib_t *cib, gboolean force);
int cli_resource_delete_attribute(pcmk_resource_t *rsc,
const char *requested_name,
const char *attr_set, const char *attr_set_type,
@@ -121,7 +131,7 @@ int cli_resource_delete_attribute(pcmk_resource_t *rsc,
cib_t *cib, int cib_options, gboolean force);
int update_scheduler_input(pcmk_scheduler_t *scheduler, xmlNode **xml);
-int wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib);
+int wait_till_stable(pcmk__output_t *out, guint timeout_ms, cib_t * cib);
bool resource_is_running_on(pcmk_resource_t *rsc, const char *host);
diff --git a/tools/crm_resource_ban.c b/tools/crm_resource_ban.c
index 3b0e4a1..689a178 100644
--- a/tools/crm_resource_ban.c
+++ b/tools/crm_resource_ban.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -72,62 +72,66 @@ cli_resource_ban(pcmk__output_t *out, const char *rsc_id, const char *host,
return EINVAL;
}
- fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
+ fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
- location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
+ location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
- out->info(out, "WARNING: Creating rsc_location constraint '%s' with a "
- "score of -INFINITY for resource %s on %s.\n\tThis will "
- "prevent %s from %s on %s until the constraint is removed "
- "using the clear option or by editing the CIB with an "
- "appropriate tool\n\tThis will be the case even if %s "
- "is the last node in the cluster",
- ID(location), rsc_id, host, rsc_id,
- (promoted_role_only? "being promoted" : "running"),
- host, host);
-
- crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
+ out->info(out,
+ "WARNING: Creating " PCMK_XE_RSC_LOCATION " constraint '%s' with "
+ "a score of " PCMK_VALUE_MINUS_INFINITY " for resource %s on %s."
+ "\n\tThis will prevent %s from %s on %s until the constraint is "
+ "removed using the clear option or by editing the CIB with an "
+ "appropriate tool.\n"
+ "\tThis will be the case even if %s is the last node in the "
+ "cluster",
+ pcmk__xe_id(location), rsc_id, host, rsc_id,
+ (promoted_role_only? "being promoted" : "running"), host, host);
+
+ crm_xml_add(location, PCMK_XA_RSC, rsc_id);
if(promoted_role_only) {
- crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role);
+ crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
} else {
- crm_xml_add(location, XML_RULE_ATTR_ROLE, PCMK__ROLE_STARTED);
+ crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED);
}
if (later_s == NULL) {
/* Short form */
- crm_xml_add(location, XML_CIB_TAG_NODE, host);
- crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
+ crm_xml_add(location, PCMK_XE_NODE, host);
+ crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY);
} else {
- xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
- xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
+ xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE);
+ xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION);
crm_xml_set_id(rule, "cli-ban-%s-on-%s-rule", rsc_id, host);
- crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_MINUS_INFINITY_S);
- crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
+ crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_MINUS_INFINITY);
+ crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
crm_xml_set_id(expr, "cli-ban-%s-on-%s-expr", rsc_id, host);
- crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
- crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
- crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
- crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
+ crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
+ crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
+ crm_xml_add(expr, PCMK_XA_VALUE, host);
+ crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
- expr = create_xml_node(rule, "date_expression");
+ expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION);
crm_xml_set_id(expr, "cli-ban-%s-on-%s-lifetime", rsc_id, host);
- crm_xml_add(expr, "operation", "lt");
- crm_xml_add(expr, "end", later_s);
+ crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
+ crm_xml_add(expr, PCMK_XA_END, later_s);
}
crm_log_xml_notice(fragment, "Modify");
- rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment,
+ rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
rc = pcmk_legacy2rc(rc);
free_xml(fragment);
free(later_s);
- if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) {
+ if ((rc != pcmk_rc_ok)
+ && promoted_role_only
+ && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) {
+
int banrc = cli_resource_ban(out, rsc_id, host, move_lifetime,
cib_conn, cib_options, promoted_role_only,
PCMK__ROLE_PROMOTED_LEGACY);
@@ -159,52 +163,55 @@ cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
return ENOTCONN;
}
- fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
+ fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
- location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
+ location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
- crm_xml_add(location, XML_LOC_ATTR_SOURCE, rsc_id);
+ crm_xml_add(location, PCMK_XA_RSC, rsc_id);
if(promoted_role_only) {
- crm_xml_add(location, XML_RULE_ATTR_ROLE, promoted_role);
+ crm_xml_add(location, PCMK_XA_ROLE, promoted_role);
} else {
- crm_xml_add(location, XML_RULE_ATTR_ROLE, PCMK__ROLE_STARTED);
+ crm_xml_add(location, PCMK_XA_ROLE, PCMK_ROLE_STARTED);
}
if (later_s == NULL) {
/* Short form */
- crm_xml_add(location, XML_CIB_TAG_NODE, host);
- crm_xml_add(location, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
+ crm_xml_add(location, PCMK_XE_NODE, host);
+ crm_xml_add(location, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
} else {
- xmlNode *rule = create_xml_node(location, XML_TAG_RULE);
- xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION);
+ xmlNode *rule = pcmk__xe_create(location, PCMK_XE_RULE);
+ xmlNode *expr = pcmk__xe_create(rule, PCMK_XE_EXPRESSION);
crm_xml_set_id(rule, "cli-prefer-rule-%s", rsc_id);
- crm_xml_add(rule, XML_RULE_ATTR_SCORE, CRM_INFINITY_S);
- crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and");
+ crm_xml_add(rule, PCMK_XA_SCORE, PCMK_VALUE_INFINITY);
+ crm_xml_add(rule, PCMK_XA_BOOLEAN_OP, PCMK_VALUE_AND);
crm_xml_set_id(expr, "cli-prefer-expr-%s", rsc_id);
- crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, CRM_ATTR_UNAME);
- crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq");
- crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host);
- crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string");
+ crm_xml_add(expr, PCMK_XA_ATTRIBUTE, CRM_ATTR_UNAME);
+ crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_EQ);
+ crm_xml_add(expr, PCMK_XA_VALUE, host);
+ crm_xml_add(expr, PCMK_XA_TYPE, PCMK_VALUE_STRING);
- expr = create_xml_node(rule, "date_expression");
+ expr = pcmk__xe_create(rule, PCMK_XE_DATE_EXPRESSION);
crm_xml_set_id(expr, "cli-prefer-lifetime-end-%s", rsc_id);
- crm_xml_add(expr, "operation", "lt");
- crm_xml_add(expr, "end", later_s);
+ crm_xml_add(expr, PCMK_XA_OPERATION, PCMK_VALUE_LT);
+ crm_xml_add(expr, PCMK_XA_END, later_s);
}
crm_log_xml_info(fragment, "Modify");
- rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment,
+ rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
cib_options);
rc = pcmk_legacy2rc(rc);
free_xml(fragment);
free(later_s);
- if (rc != pcmk_rc_ok && promoted_role_only && strcmp(promoted_role, PCMK__ROLE_PROMOTED) == 0) {
+ if ((rc != pcmk_rc_ok)
+ && promoted_role_only
+ && (strcmp(promoted_role, PCMK_ROLE_PROMOTED) == 0)) {
+
int preferrc = cli_resource_prefer(out, rsc_id, host, move_lifetime,
cib_conn, cib_options, promoted_role_only,
PCMK__ROLE_PROMOTED_LEGACY);
@@ -229,8 +236,9 @@ cli_resource_prefer(pcmk__output_t *out,const char *rsc_id, const char *host,
* </rule>
* </rsc_location>
*
- * (2) The mode could be given by node= in an rsc_location XML node. That's
- * what resource_clear_node_in_location handles. That XML looks like this:
+ * (2) The node could be given by node= in a PCMK_XE_RSC_LOCATION XML node.
+ * That's what resource_clear_node_in_location handles. That XML looks like
+ * this:
*
* <rsc_location id="cli-prefer-dummy" rsc="dummy" role="Started" node="node1" score="INFINITY"/>
*
@@ -243,13 +251,13 @@ resource_clear_node_in_expr(const char *rsc_id, const char *host, cib_t * cib_co
int rc = pcmk_rc_ok;
char *xpath_string = NULL;
-#define XPATH_FMT \
- "//" XML_CONS_TAG_RSC_LOCATION "[@" XML_ATTR_ID "='cli-prefer-%s']" \
- "[" XML_TAG_RULE \
- "[@" XML_ATTR_ID "='cli-prefer-rule-%s']" \
- "/" XML_TAG_EXPRESSION \
- "[@" XML_EXPR_ATTR_ATTRIBUTE "='#uname' " \
- "and @" XML_EXPR_ATTR_VALUE "='%s']" \
+#define XPATH_FMT \
+ "//" PCMK_XE_RSC_LOCATION "[@" PCMK_XA_ID "='cli-prefer-%s']" \
+ "[" PCMK_XE_RULE \
+ "[@" PCMK_XA_ID "='cli-prefer-rule-%s']" \
+ "/" PCMK_XE_EXPRESSION \
+ "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " \
+ "and @" PCMK_XA_VALUE "='%s']" \
"]"
xpath_string = crm_strdup_printf(XPATH_FMT, rsc_id, rsc_id, host);
@@ -274,21 +282,22 @@ resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * ci
xmlNode *fragment = NULL;
xmlNode *location = NULL;
- fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
+ fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
if (clear_ban_constraints == TRUE) {
- location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
+ location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-ban-%s-on-%s", rsc_id, host);
}
- location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
+ location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
crm_xml_set_id(location, "cli-prefer-%s", rsc_id);
if (force == FALSE) {
- crm_xml_add(location, XML_CIB_TAG_NODE, host);
+ crm_xml_add(location, PCMK_XE_NODE, host);
}
crm_log_xml_info(fragment, "Delete");
- rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options);
+ rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
+ cib_options);
if (rc == -ENXIO) {
rc = pcmk_rc_ok;
} else {
@@ -349,12 +358,11 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
const char *rsc, const char *node,
bool promoted_role_only)
{
- const char *cons_id = ID(constraint_node);
- const char *cons_rsc = crm_element_value(constraint_node,
- XML_LOC_ATTR_SOURCE);
+ const char *cons_id = pcmk__xe_id(constraint_node);
+ const char *cons_rsc = crm_element_value(constraint_node, PCMK_XA_RSC);
GString *rsc_role_substr = NULL;
- const char *promoted_role_rule = "@" XML_RULE_ATTR_ROLE "='" PCMK__ROLE_PROMOTED
- "' or @" XML_RULE_ATTR_ROLE "='"
+ const char *promoted_role_rule = "@" PCMK_XA_ROLE "='" PCMK_ROLE_PROMOTED
+ "' or @" PCMK_XA_ROLE "='"
PCMK__ROLE_PROMOTED_LEGACY "'";
CRM_ASSERT(buf != NULL);
@@ -365,13 +373,13 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
return;
}
- g_string_append(buf, "//" XML_CONS_TAG_RSC_LOCATION);
+ g_string_append(buf, "//" PCMK_XE_RSC_LOCATION);
if ((node != NULL) || (rsc != NULL) || promoted_role_only) {
g_string_append_c(buf, '[');
if (node != NULL) {
- pcmk__g_strcat(buf, "@" XML_CIB_TAG_NODE "='", node, "'", NULL);
+ pcmk__g_strcat(buf, "@" PCMK_XE_NODE "='", node, "'", NULL);
if (promoted_role_only || (rsc != NULL)) {
g_string_append(buf, " and ");
@@ -381,13 +389,13 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
if ((rsc != NULL) && promoted_role_only) {
rsc_role_substr = g_string_sized_new(64);
pcmk__g_strcat(rsc_role_substr,
- "@" XML_LOC_ATTR_SOURCE "='", rsc, "' "
+ "@" PCMK_XA_RSC "='", rsc, "' "
"and (" , promoted_role_rule, ")", NULL);
} else if (rsc != NULL) {
rsc_role_substr = g_string_sized_new(64);
pcmk__g_strcat(rsc_role_substr,
- "@" XML_LOC_ATTR_SOURCE "='", rsc, "'", NULL);
+ "@" PCMK_XA_RSC "='", rsc, "'", NULL);
} else if (promoted_role_only) {
rsc_role_substr = g_string_sized_new(64);
@@ -401,18 +409,18 @@ build_clear_xpath_string(GString *buf, const xmlNode *constraint_node,
}
if (node != NULL) {
- g_string_append(buf, "|//" XML_CONS_TAG_RSC_LOCATION);
+ g_string_append(buf, "|//" PCMK_XE_RSC_LOCATION);
if (rsc_role_substr != NULL) {
pcmk__g_strcat(buf, "[", rsc_role_substr, "]", NULL);
}
pcmk__g_strcat(buf,
- "/" XML_TAG_RULE "[" XML_TAG_EXPRESSION
- "[@" XML_EXPR_ATTR_ATTRIBUTE "='" CRM_ATTR_UNAME "' "
- "and @" XML_EXPR_ATTR_VALUE "='", node, "']]", NULL);
+ "/" PCMK_XE_RULE "[" PCMK_XE_EXPRESSION
+ "[@" PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' "
+ "and @" PCMK_XA_VALUE "='", node, "']]", NULL);
}
- g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" XML_ATTR_ID "='");
+ g_string_append(buf, "//" PCMK_XE_DATE_EXPRESSION "[@" PCMK_XA_ID "='");
if (pcmk__starts_with(cons_id, "cli-ban-")) {
pcmk__g_strcat(buf, cons_id, "-lifetime']", NULL);
@@ -438,13 +446,14 @@ cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options,
int i;
int rc = pcmk_rc_ok;
- cib_constraints = pcmk_find_cib_element(root, XML_CIB_TAG_CONSTRAINTS);
- xpathObj = xpath_search(cib_constraints, "//" XML_CONS_TAG_RSC_LOCATION);
+ cib_constraints = pcmk_find_cib_element(root, PCMK_XE_CONSTRAINTS);
+ xpathObj = xpath_search(cib_constraints, "//" PCMK_XE_RSC_LOCATION);
for (i = 0; i < numXpathResults(xpathObj); i++) {
xmlNode *constraint_node = getXpathResult(xpathObj, i);
xmlNode *date_expr_node = NULL;
crm_time_t *end = NULL;
+ int rc = pcmk_rc_ok;
if (buf == NULL) {
buf = g_string_sized_new(1024);
@@ -464,20 +473,25 @@ cli_resource_clear_all_expired(xmlNode *root, cib_t *cib_conn, int cib_options,
/* And then finally, see if the date expression is expired. If so,
* clear the constraint.
+ *
+ * @COMPAT Check for error once we are rejecting rules with invalid end
*/
- end = crm_time_new(crm_element_value(date_expr_node, "end"));
+ rc = pcmk__xe_get_datetime(date_expr_node, PCMK_XA_END, &end);
+ if (rc != pcmk_rc_ok) {
+ crm_trace("Invalid " PCMK_XA_END ": %s", pcmk_rc_str(rc));
+ }
if (crm_time_compare(now, end) == 1) {
xmlNode *fragment = NULL;
xmlNode *location = NULL;
- fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS);
- location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION);
- crm_xml_set_id(location, "%s", ID(constraint_node));
+ fragment = pcmk__xe_create(NULL, PCMK_XE_CONSTRAINTS);
+ location = pcmk__xe_create(fragment, PCMK_XE_RSC_LOCATION);
+ crm_xml_set_id(location, "%s", pcmk__xe_id(constraint_node));
crm_log_xml_info(fragment, "Delete");
- rc = cib_conn->cmds->remove(cib_conn, XML_CIB_TAG_CONSTRAINTS,
- fragment, cib_options);
+ rc = cib_conn->cmds->remove(cib_conn, PCMK_XE_CONSTRAINTS, fragment,
+ cib_options);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index bdf3ad9..811bab0 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -23,30 +23,33 @@ print_constraint(xmlNode *xml_obj, void *userdata)
pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) userdata;
pcmk__output_t *out = scheduler->priv;
xmlNode *lifetime = NULL;
- const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
+ const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
+ pcmk_rule_input_t rule_input = {
+ .now = scheduler->now,
+ };
if (id == NULL) {
return pcmk_rc_ok;
}
- // @COMPAT lifetime is deprecated
- lifetime = first_named_child(xml_obj, "lifetime");
- if (pe_evaluate_rules(lifetime, NULL, scheduler->now, NULL) == FALSE) {
+ // @COMPAT PCMK__XE_LIFETIME is deprecated
+ lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL);
+ if (pcmk__evaluate_rules(lifetime, &rule_input, NULL) != pcmk_rc_ok) {
return pcmk_rc_ok;
}
- if (!pcmk__xe_is(xml_obj, XML_CONS_TAG_RSC_DEPEND)) {
+ if (!pcmk__xe_is(xml_obj, PCMK_XE_RSC_COLOCATION)) {
return pcmk_rc_ok;
}
out->info(out, "Constraint %s %s %s %s %s %s %s",
xml_obj->name,
- cons_string(crm_element_value(xml_obj, XML_ATTR_ID)),
- cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)),
- cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)),
- cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)),
- cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)),
- cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE)));
+ cons_string(crm_element_value(xml_obj, PCMK_XA_ID)),
+ cons_string(crm_element_value(xml_obj, PCMK_XA_RSC)),
+ cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC)),
+ cons_string(crm_element_value(xml_obj, PCMK_XA_SCORE)),
+ cons_string(crm_element_value(xml_obj, PCMK_XA_RSC_ROLE)),
+ cons_string(crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE)));
return pcmk_rc_ok;
}
@@ -55,7 +58,7 @@ void
cli_resource_print_cts_constraints(pcmk_scheduler_t *scheduler)
{
pcmk__xe_foreach_child(pcmk_find_cib_element(scheduler->input,
- XML_CIB_TAG_CONSTRAINTS),
+ PCMK_XE_CONSTRAINTS),
NULL, print_constraint, scheduler);
}
@@ -64,10 +67,10 @@ cli_resource_print_cts(pcmk_resource_t *rsc, pcmk__output_t *out)
{
const char *host = NULL;
bool needs_quorum = TRUE;
- const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
- const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
- const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
- pcmk_node_t *node = pe__current_node(rsc);
+ const char *rtype = crm_element_value(rsc->xml, PCMK_XA_TYPE);
+ const char *rprov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
+ const char *rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS);
+ pcmk_node_t *node = pcmk__current_node(rsc);
if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
needs_quorum = FALSE;
@@ -125,7 +128,8 @@ cli_resource_print(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler,
all = g_list_prepend(all, (gpointer) "*");
out->begin_list(out, NULL, NULL, "Resource Config");
- out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc, all, all);
+ out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc, all,
+ all);
out->message(out, "resource-config", rsc, !expanded);
out->end_list(out);
@@ -133,6 +137,88 @@ cli_resource_print(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler,
return pcmk_rc_ok;
}
+PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *")
+static int
+attribute_changed_default(pcmk__output_t *out, va_list args)
+{
+ attr_update_data_t *ud = va_arg(args, attr_update_data_t *);
+
+ out->info(out, "Set '%s' option: "
+ PCMK_XA_ID "=%s%s%s%s%s value=%s",
+ ud->given_rsc_id, ud->found_attr_id,
+ ((ud->attr_set_id == NULL)? "" : " " PCMK__XA_SET "="),
+ pcmk__s(ud->attr_set_id, ""),
+ ((ud->attr_name == NULL)? "" : " " PCMK_XA_NAME "="),
+ pcmk__s(ud->attr_name, ""), ud->attr_value);
+
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *")
+static int
+attribute_changed_xml(pcmk__output_t *out, va_list args)
+{
+ attr_update_data_t *ud = va_arg(args, attr_update_data_t *);
+
+ pcmk__output_xml_create_parent(out, (const char *) ud->rsc->xml->name,
+ PCMK_XA_ID, ud->rsc->id,
+ NULL);
+
+ pcmk__output_xml_create_parent(out, ud->attr_set_type,
+ PCMK_XA_ID, ud->attr_set_id,
+ NULL);
+
+ pcmk__output_create_xml_node(out, PCMK_XE_NVPAIR,
+ PCMK_XA_ID, ud->found_attr_id,
+ PCMK_XA_VALUE, ud->attr_value,
+ PCMK_XA_NAME, ud->attr_name,
+ NULL);
+
+ pcmk__output_xml_pop_parent(out);
+ pcmk__output_xml_pop_parent(out);
+
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *")
+static int
+attribute_changed_list_default(pcmk__output_t *out, va_list args)
+{
+ GList *results = va_arg(args, GList *);
+
+ if (results == NULL) {
+ return pcmk_rc_no_output;
+ }
+
+ for (GList *iter = results; iter != NULL; iter = iter->next) {
+ attr_update_data_t *ud = iter->data;
+ out->message(out, "attribute-changed", ud);
+ }
+
+ return pcmk_rc_ok;
+}
+
+PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *")
+static int
+attribute_changed_list_xml(pcmk__output_t *out, va_list args)
+{
+ GList *results = va_arg(args, GList *);
+
+ if (results == NULL) {
+ return pcmk_rc_no_output;
+ }
+
+ pcmk__output_xml_create_parent(out, PCMK__XE_RESOURCE_SETTINGS, NULL);
+
+ for (GList *iter = results; iter != NULL; iter = iter->next) {
+ attr_update_data_t *ud = iter->data;
+ out->message(out, "attribute-changed", ud);
+ }
+
+ pcmk__output_xml_pop_parent(out);
+ return pcmk_rc_ok;
+}
+
PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *",
"const char *")
static int
@@ -210,19 +296,21 @@ agent_status_xml(pcmk__output_t *out, va_list args) {
crm_exit_t rc = va_arg(args, crm_exit_t);
const char *exit_reason = va_arg(args, const char *);
- char *exit_str = pcmk__itoa(rc);
- char *status_str = pcmk__itoa(status);
-
- pcmk__output_create_xml_node(out, "agent-status",
- "code", exit_str,
- "message", services_ocf_exitcode_str((int) rc),
- "execution_code", status_str,
- "execution_message", pcmk_exec_status_str(status),
- "reason", exit_reason,
+ char *exit_s = pcmk__itoa(rc);
+ const char *message = services_ocf_exitcode_str((int) rc);
+ char *status_s = pcmk__itoa(status);
+ const char *execution_message = pcmk_exec_status_str(status);
+
+ pcmk__output_create_xml_node(out, PCMK_XE_AGENT_STATUS,
+ PCMK_XA_CODE, exit_s,
+ PCMK_XA_MESSAGE, message,
+ PCMK_XA_EXECUTION_CODE, status_s,
+ PCMK_XA_EXECUTION_MESSAGE, execution_message,
+ PCMK_XA_REASON, exit_reason,
NULL);
- free(exit_str);
- free(status_str);
+ free(exit_s);
+ free(status_s);
return pcmk_rc_ok;
}
@@ -268,13 +356,13 @@ override_xml(pcmk__output_t *out, va_list args) {
const char *name = va_arg(args, const char *);
const char *value = va_arg(args, const char *);
- xmlNodePtr node = pcmk__output_create_xml_node(out, "override",
- "name", name,
- "value", value,
+ xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_OVERRIDE,
+ PCMK_XA_NAME, name,
+ PCMK_XA_VALUE, value,
NULL);
if (rsc_name != NULL) {
- crm_xml_add(node, "rsc", rsc_name);
+ crm_xml_add(node, PCMK_XA_RSC, rsc_name);
}
return pcmk_rc_ok;
@@ -336,7 +424,7 @@ resource_agent_action_default(pcmk__output_t *out, va_list args) {
const char *name = NULL;
const char *value = NULL;
- out->begin_list(out, NULL, NULL, "overrides");
+ out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES);
g_hash_table_iter_init(&iter, overrides);
while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
@@ -359,10 +447,10 @@ resource_agent_action_default(pcmk__output_t *out, va_list args) {
xmlNodePtr doc = NULL;
if (stdout_data != NULL) {
- doc = string2xml(stdout_data);
+ doc = pcmk__xml_parse(stdout_data);
}
if (doc != NULL) {
- out->output_xml(out, "command", stdout_data);
+ out->output_xml(out, PCMK_XE_COMMAND, stdout_data);
xmlFreeNode(doc);
} else {
out->subprocess_output(out, rc, stdout_data, stderr_data);
@@ -391,26 +479,26 @@ resource_agent_action_xml(pcmk__output_t *out, va_list args) {
const char *stdout_data = va_arg(args, const char *);
const char *stderr_data = va_arg(args, const char *);
- xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action",
- "action", action,
- "class", class,
- "type", type,
- NULL);
+ xmlNodePtr node = NULL;
+
+ node = pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT_ACTION,
+ PCMK_XA_ACTION, action,
+ PCMK_XA_CLASS, class,
+ PCMK_XA_TYPE, type,
+ NULL);
if (rsc_name) {
- crm_xml_add(node, "rsc", rsc_name);
+ crm_xml_add(node, PCMK_XA_RSC, rsc_name);
}
- if (provider) {
- crm_xml_add(node, "provider", provider);
- }
+ crm_xml_add(node, PCMK_XA_PROVIDER, provider);
if (overrides) {
GHashTableIter iter;
const char *name = NULL;
const char *value = NULL;
- out->begin_list(out, NULL, NULL, "overrides");
+ out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES);
g_hash_table_iter_init(&iter, overrides);
while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
@@ -427,10 +515,10 @@ resource_agent_action_xml(pcmk__output_t *out, va_list args) {
xmlNodePtr doc = NULL;
if (stdout_data != NULL) {
- doc = string2xml(stdout_data);
+ doc = pcmk__xml_parse(stdout_data);
}
if (doc != NULL) {
- out->output_xml(out, "command", stdout_data);
+ out->output_xml(out, PCMK_XE_COMMAND, stdout_data);
xmlFreeNode(doc);
} else {
out->subprocess_output(out, rc, stdout_data, stderr_data);
@@ -477,10 +565,10 @@ resource_check_list_default(pcmk__output_t *out, va_list args) {
if (pcmk_is_set(checks->flags, rsc_node_health)) {
out->list_item(out, "check",
"'%s' cannot run on unhealthy nodes due to "
- PCMK__OPT_NODE_HEALTH_STRATEGY "='%s'",
+ PCMK_OPT_NODE_HEALTH_STRATEGY "='%s'",
parent->id,
- pe_pref(checks->rsc->cluster->config_hash,
- PCMK__OPT_NODE_HEALTH_STRATEGY));
+ pcmk__cluster_option(checks->rsc->cluster->config_hash,
+ PCMK_OPT_NODE_HEALTH_STRATEGY));
}
out->end_list(out);
@@ -494,39 +582,39 @@ resource_check_list_xml(pcmk__output_t *out, va_list args) {
const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false);
- xmlNodePtr node = pcmk__output_create_xml_node(out, "check",
- "id", parent->id,
+ xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_CHECK,
+ PCMK_XA_ID, parent->id,
NULL);
if (pcmk_is_set(checks->flags, rsc_remain_stopped)) {
- pcmk__xe_set_bool_attr(node, "remain_stopped", true);
+ pcmk__xe_set_bool_attr(node, PCMK_XA_REMAIN_STOPPED, true);
}
if (pcmk_is_set(checks->flags, rsc_unpromotable)) {
- pcmk__xe_set_bool_attr(node, "promotable", false);
+ pcmk__xe_set_bool_attr(node, PCMK_XA_PROMOTABLE, false);
}
if (pcmk_is_set(checks->flags, rsc_unmanaged)) {
- pcmk__xe_set_bool_attr(node, "unmanaged", true);
+ pcmk__xe_set_bool_attr(node, PCMK_XA_UNMANAGED, true);
}
if (pcmk_is_set(checks->flags, rsc_locked)) {
- crm_xml_add(node, "locked-to", checks->lock_node);
+ crm_xml_add(node, PCMK_XA_LOCKED_TO_HYPHEN, checks->lock_node);
}
if (pcmk_is_set(checks->flags, rsc_node_health)) {
- pcmk__xe_set_bool_attr(node, "unhealthy", true);
+ pcmk__xe_set_bool_attr(node, PCMK_XA_UNHEALTHY, true);
}
return pcmk_rc_ok;
}
-PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const char *")
+PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *")
static int
resource_search_list_default(pcmk__output_t *out, va_list args)
{
GList *nodes = va_arg(args, GList *);
- const char *requested_name = va_arg(args, const char *);
+ const gchar *requested_name = va_arg(args, const gchar *);
bool printed = false;
int rc = pcmk_rc_no_output;
@@ -554,7 +642,7 @@ resource_search_list_default(pcmk__output_t *out, va_list args)
#ifdef PCMK__COMPAT_2_0
role_text = " " PCMK__ROLE_PROMOTED_LEGACY;
#else
- role_text = " " PCMK__ROLE_PROMOTED;
+ role_text = " " PCMK_ROLE_PROMOTED;
#endif
}
out->list_item(out, "node", "resource %s is running on: %s%s",
@@ -569,26 +657,29 @@ resource_search_list_default(pcmk__output_t *out, va_list args)
return rc;
}
-PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const char *")
+PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *")
static int
resource_search_list_xml(pcmk__output_t *out, va_list args)
{
GList *nodes = va_arg(args, GList *);
- const char *requested_name = va_arg(args, const char *);
+ const gchar *requested_name = va_arg(args, const gchar *);
- pcmk__output_xml_create_parent(out, "nodes",
- "resource", requested_name,
+ pcmk__output_xml_create_parent(out, PCMK_XE_NODES,
+ PCMK_XA_RESOURCE, requested_name,
NULL);
for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) {
node_info_t *ni = (node_info_t *) lpc->data;
- xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out, "node", ni->node_name);
+ xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out,
+ PCMK_XE_NODE,
+ ni->node_name);
if (ni->promoted) {
- crm_xml_add(sub_node, "state", "promoted");
+ crm_xml_add(sub_node, PCMK_XA_STATE, "promoted");
}
}
+ pcmk__output_xml_pop_parent(out);
return pcmk_rc_ok;
}
@@ -685,22 +776,25 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
const char *host_uname = (node == NULL)? NULL : node->details->uname;
- xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, "reason", NULL);
+ xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, PCMK_XE_REASON,
+ NULL);
if ((rsc == NULL) && (host_uname == NULL)) {
GList *lpc = NULL;
GList *hosts = NULL;
- pcmk__output_xml_create_parent(out, "resources", NULL);
+ pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
for (lpc = resources; lpc != NULL; lpc = lpc->next) {
pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
+ const char *running = NULL;
rsc->fns->location(rsc, &hosts, TRUE);
+ running = pcmk__btoa(hosts != NULL);
- pcmk__output_xml_create_parent(out, "resource",
- "id", rsc->id,
- "running", pcmk__btoa(hosts != NULL),
+ pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
+ PCMK_XA_ID, rsc->id,
+ PCMK_XA_RUNNING, running,
NULL);
cli_resource_check(out, rsc, NULL);
@@ -713,7 +807,7 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
} else if ((rsc != NULL) && (host_uname != NULL)) {
if (resource_is_running_on(rsc, host_uname)) {
- crm_xml_add(xml_node, "running_on", host_uname);
+ crm_xml_add(xml_node, PCMK_XA_RUNNING_ON, host_uname);
}
cli_resource_check(out, rsc, node);
@@ -725,15 +819,15 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp);
GList *lpc = NULL;
- pcmk__output_xml_create_parent(out, "resources", NULL);
+ pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
for (lpc = activeResources; lpc != NULL; lpc = lpc->next) {
pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
- pcmk__output_xml_create_parent(out, "resource",
- "id", rsc->id,
- "running", "true",
- "host", host_uname,
+ pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
+ PCMK_XA_ID, rsc->id,
+ PCMK_XA_RUNNING, PCMK_VALUE_TRUE,
+ PCMK_XA_HOST, host_uname,
NULL);
cli_resource_check(out, rsc, node);
@@ -743,10 +837,10 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) {
pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
- pcmk__output_xml_create_parent(out, "resource",
- "id", rsc->id,
- "running", "false",
- "host", host_uname,
+ pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
+ PCMK_XA_ID, rsc->id,
+ PCMK_XA_RUNNING, PCMK_VALUE_FALSE,
+ PCMK_XA_HOST, host_uname,
NULL);
cli_resource_check(out, rsc, node);
@@ -762,11 +856,12 @@ resource_reasons_list_xml(pcmk__output_t *out, va_list args)
GList *hosts = NULL;
rsc->fns->location(rsc, &hosts, TRUE);
- crm_xml_add(xml_node, "running", pcmk__btoa(hosts != NULL));
+ crm_xml_add(xml_node, PCMK_XA_RUNNING, pcmk__btoa(hosts != NULL));
cli_resource_check(out, rsc, NULL);
g_list_free(hosts);
}
+ pcmk__output_xml_pop_parent(out);
return pcmk_rc_ok;
}
@@ -774,7 +869,11 @@ static void
add_resource_name(pcmk_resource_t *rsc, pcmk__output_t *out)
{
if (rsc->children == NULL) {
- out->list_item(out, "resource", "%s", rsc->id);
+ /* Sometimes PCMK_XE_RESOURCE might act as a PCMK_XA_NAME instead of an
+ * XML element name, depending on whether pcmk__output_enable_list_element
+ * was called.
+ */
+ out->list_item(out, PCMK_XE_RESOURCE, "%s", rsc->id);
} else {
g_list_foreach(rsc->children, (GFunc) add_resource_name, out);
}
@@ -799,6 +898,10 @@ resource_names(pcmk__output_t *out, va_list args) {
static pcmk__message_entry_t fmt_functions[] = {
{ "agent-status", "default", agent_status_default },
{ "agent-status", "xml", agent_status_xml },
+ { "attribute-changed", "default", attribute_changed_default },
+ { "attribute-changed", "xml", attribute_changed_xml },
+ { "attribute-changed-list", "default", attribute_changed_list_default },
+ { "attribute-changed-list", "xml", attribute_changed_list_xml },
{ "attribute-list", "default", attribute_list_default },
{ "attribute-list", "text", attribute_list_text },
{ "override", "default", override_default },
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index da360fd..5b4fd2b 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -9,12 +9,18 @@
#include <crm_internal.h>
-#include <crm_resource.h>
+#include <stdio.h>
+#include <limits.h>
+#include <glib.h>
+#include <libxml/tree.h>
+
#include <crm/common/ipc_attrd_internal.h>
#include <crm/common/ipc_controld.h>
#include <crm/common/lists_internal.h>
#include <crm/services_internal.h>
+#include <crm_resource.h>
+
static GList *
build_node_info_list(const pcmk_resource_t *rsc)
{
@@ -27,7 +33,7 @@ build_node_info_list(const pcmk_resource_t *rsc)
iter2 != NULL; iter2 = iter2->next) {
const pcmk_node_t *node = (const pcmk_node_t *) iter2->data;
- node_info_t *ni = calloc(1, sizeof(node_info_t));
+ node_info_t *ni = pcmk__assert_alloc(1, sizeof(node_info_t));
ni->node_name = node->details->uname;
ni->promoted = pcmk_is_set(rsc->flags, pcmk_rsc_promotable) &&
@@ -47,22 +53,23 @@ cli_resource_search(pcmk_resource_t *rsc, const char *requested_name,
GList *retval = NULL;
const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
- if (pe_rsc_is_clone(rsc)) {
+ if (pcmk__is_clone(rsc)) {
retval = build_node_info_list(rsc);
/* The anonymous clone children's common ID is supplied */
- } else if (pe_rsc_is_clone(parent)
+ } else if (pcmk__is_clone(parent)
&& !pcmk_is_set(rsc->flags, pcmk_rsc_unique)
&& rsc->clone_name
- && pcmk__str_eq(requested_name, rsc->clone_name, pcmk__str_casei)
- && !pcmk__str_eq(requested_name, rsc->id, pcmk__str_casei)) {
+ && pcmk__str_eq(requested_name, rsc->clone_name, pcmk__str_none)
+ && !pcmk__str_eq(requested_name, rsc->id, pcmk__str_none)) {
retval = build_node_info_list(parent);
} else if (rsc->running_on != NULL) {
for (GList *iter = rsc->running_on; iter != NULL; iter = iter->next) {
pcmk_node_t *node = (pcmk_node_t *) iter->data;
- node_info_t *ni = calloc(1, sizeof(node_info_t));
+ node_info_t *ni = pcmk__assert_alloc(1, sizeof(node_info_t));
+
ni->node_name = node->details->uname;
ni->promoted = (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted);
@@ -77,83 +84,73 @@ cli_resource_search(pcmk_resource_t *rsc, const char *requested_name,
static int
find_resource_attr(pcmk__output_t *out, cib_t * the_cib, const char *attr,
const char *rsc, const char *attr_set_type, const char *set_name,
- const char *attr_id, const char *attr_name, char **value)
+ const char *attr_id, const char *attr_name, xmlNode **result)
{
+ xmlNode *xml_search;
int rc = pcmk_rc_ok;
- xmlNode *xml_search = NULL;
GString *xpath = NULL;
const char *xpath_base = NULL;
- if(value) {
- *value = NULL;
+ if (result) {
+ *result = NULL;
}
if(the_cib == NULL) {
return ENOTCONN;
}
- xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_RESOURCES);
+ xpath_base = pcmk_cib_xpath_for(PCMK_XE_RESOURCES);
if (xpath_base == NULL) {
- crm_err(XML_CIB_TAG_RESOURCES " CIB element not known (bug?)");
+ crm_err(PCMK_XE_RESOURCES " CIB element not known (bug?)");
return ENOMSG;
}
xpath = g_string_sized_new(1024);
pcmk__g_strcat(xpath,
- xpath_base, "//*[@" XML_ATTR_ID "=\"", rsc, "\"]", NULL);
+ xpath_base, "//*[@" PCMK_XA_ID "=\"", rsc, "\"]", NULL);
if (attr_set_type != NULL) {
pcmk__g_strcat(xpath, "/", attr_set_type, NULL);
if (set_name != NULL) {
- pcmk__g_strcat(xpath, "[@" XML_ATTR_ID "=\"", set_name, "\"]",
+ pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "=\"", set_name, "\"]",
NULL);
}
}
- g_string_append(xpath, "//" XML_CIB_TAG_NVPAIR "[");
- if (attr_id != NULL) {
- pcmk__g_strcat(xpath, "@" XML_ATTR_ID "=\"", attr_id, "\"", NULL);
- }
+ g_string_append(xpath, "//" PCMK_XE_NVPAIR);
- if (attr_name != NULL) {
- if (attr_id != NULL) {
- g_string_append(xpath, " and ");
- }
- pcmk__g_strcat(xpath, "@" XML_NVPAIR_ATTR_NAME "=\"", attr_name, "\"",
- NULL);
+ if (attr_id != NULL && attr_name!= NULL) {
+ pcmk__g_strcat(xpath,
+ "[@" PCMK_XA_ID "='", attr_id, "' "
+ "and @" PCMK_XA_NAME "='", attr_name, "']", NULL);
+
+ } else if (attr_id != NULL) {
+ pcmk__g_strcat(xpath, "[@" PCMK_XA_ID "='", attr_id, "']", NULL);
+
+ } else if (attr_name != NULL) {
+ pcmk__g_strcat(xpath, "[@" PCMK_XA_NAME "='", attr_name, "']", NULL);
}
- g_string_append_c(xpath, ']');
rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
cib_sync_call | cib_scope_local | cib_xpath);
rc = pcmk_legacy2rc(rc);
- if (rc != pcmk_rc_ok) {
- goto done;
- }
-
- crm_log_xml_debug(xml_search, "Match");
- if (xml_search->children != NULL) {
- xmlNode *child = NULL;
-
- rc = ENOTUNIQ;
- out->info(out, "Multiple attributes match name=%s", attr_name);
-
- for (child = pcmk__xml_first_child(xml_search); child != NULL;
- child = pcmk__xml_next(child)) {
- out->info(out, " Value: %s \t(id=%s)",
- crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child));
+ if (rc == pcmk_rc_ok) {
+ crm_log_xml_debug(xml_search, "Match");
+ if (xml_search->children != NULL) {
+ rc = ENOTUNIQ;
+ pcmk__warn_multiple_name_matches(out, xml_search, attr_name);
+ out->spacer(out);
}
+ }
- out->spacer(out);
-
- } else if(value) {
- pcmk__str_update(value, crm_element_value(xml_search, attr));
+ if (result) {
+ *result = xml_search;
+ } else {
+ free_xml(xml_search);
}
- done:
g_string_free(xpath, TRUE);
- free_xml(xml_search);
return rc;
}
@@ -161,29 +158,27 @@ find_resource_attr(pcmk__output_t *out, cib_t * the_cib, const char *attr,
static void
find_matching_attr_resources_recursive(pcmk__output_t *out,
GList /* <pcmk_resource_t*> */ **result,
- pcmk_resource_t *rsc, const char *rsc_id,
- const char * attr_set, const char * attr_set_type,
- const char * attr_id, const char * attr_name,
- cib_t * cib, const char * cmd, int depth)
+ pcmk_resource_t *rsc, const char * attr_set,
+ const char * attr_set_type, const char * attr_id,
+ const char * attr_name, cib_t * cib, int depth)
{
int rc = pcmk_rc_ok;
char *lookup_id = clone_strip(rsc->id);
- char *local_attr_id = NULL;
/* visit the children */
for(GList *gIter = rsc->children; gIter; gIter = gIter->next) {
find_matching_attr_resources_recursive(out, result,
(pcmk_resource_t *) gIter->data,
- rsc_id, attr_set, attr_set_type,
- attr_id, attr_name, cib, cmd, depth+1);
+ attr_set, attr_set_type, attr_id,
+ attr_name, cib, depth+1);
/* do it only once for clones */
- if (rsc->variant == pcmk_rsc_variant_clone) {
+ if (pcmk__is_clone(rsc)) {
break;
}
}
- rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type,
- attr_set, attr_id, attr_name, &local_attr_id);
+ rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type,
+ attr_set, attr_id, attr_name, NULL);
/* Post-order traversal.
* The root is always on the list and it is the last item. */
if((0 == depth) || (pcmk_rc_ok == rc)) {
@@ -191,7 +186,6 @@ find_matching_attr_resources_recursive(pcmk__output_t *out,
*result = g_list_append(*result, rsc);
}
- free(local_attr_id);
free(lookup_id);
}
@@ -206,7 +200,6 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc,
{
int rc = pcmk_rc_ok;
char *lookup_id = NULL;
- char *local_attr_id = NULL;
GList * result = NULL;
/* If --force is used, update only the requested resource (clone or primitive).
* Otherwise, if the primitive has the attribute, use that.
@@ -214,13 +207,9 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc,
if(force == TRUE) {
return g_list_append(result, rsc);
}
- if ((rsc->parent != NULL)
- && (rsc->parent->variant == pcmk_rsc_variant_clone)) {
- int rc = pcmk_rc_ok;
- char *local_attr_id = NULL;
- rc = find_resource_attr(out, cib, XML_ATTR_ID, rsc_id, attr_set_type,
- attr_set, attr_id, attr_name, &local_attr_id);
- free(local_attr_id);
+ if (pcmk__is_clone(rsc->parent)) {
+ int rc = find_resource_attr(out, cib, PCMK_XA_ID, rsc_id, attr_set_type,
+ attr_set, attr_id, attr_name, NULL);
if(rc != pcmk_rc_ok) {
rsc = rsc->parent;
@@ -230,13 +219,13 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc,
return g_list_append(result, rsc);
} else if ((rsc->parent == NULL) && (rsc->children != NULL)
- && (rsc->variant == pcmk_rsc_variant_clone)) {
+ && pcmk__is_clone(rsc)) {
pcmk_resource_t *child = rsc->children->data;
- if (child->variant == pcmk_rsc_variant_primitive) {
+ if (pcmk__is_primitive(child)) {
lookup_id = clone_strip(child->id); /* Could be a cloned group! */
- rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type,
- attr_set, attr_id, attr_name, &local_attr_id);
+ rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id,
+ attr_set_type, attr_set, attr_id, attr_name, NULL);
if(rc == pcmk_rc_ok) {
rsc = child;
@@ -244,110 +233,167 @@ find_matching_attr_resources(pcmk__output_t *out, pcmk_resource_t *rsc,
attr_name, lookup_id, cmd, rsc_id);
}
- free(local_attr_id);
free(lookup_id);
}
return g_list_append(result, rsc);
}
/* If the resource is a group ==> children inherit the attribute if defined. */
- find_matching_attr_resources_recursive(out, &result, rsc, rsc_id, attr_set,
+ find_matching_attr_resources_recursive(out, &result, rsc, attr_set,
attr_set_type, attr_id, attr_name,
- cib, cmd, 0);
+ cib, 0);
return result;
}
-// \return Standard Pacemaker return code
-int
-cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name,
- const char *attr_set, const char *attr_set_type,
- const char *attr_id, const char *attr_name,
- const char *attr_value, gboolean recursive,
- cib_t *cib, int cib_options, gboolean force)
+static int
+update_element_attribute(pcmk__output_t *out, pcmk_resource_t *rsc,
+ cib_t *cib, const char *attr_name, const char *attr_value)
{
- pcmk__output_t *out = rsc->cluster->priv;
int rc = pcmk_rc_ok;
- char *found_attr_id = NULL;
+ if (cib == NULL) {
+ return ENOTCONN;
+ }
- GList/*<pcmk_resource_t*>*/ *resources = NULL;
- const char *top_id = pe__const_top_resource(rsc, false)->id;
+ crm_xml_add(rsc->xml, attr_name, attr_value);
- if ((attr_id == NULL) && !force) {
- find_resource_attr(out, cib, XML_ATTR_ID, top_id, NULL, NULL, NULL,
- attr_name, NULL);
+ rc = cib->cmds->replace(cib, PCMK_XE_RESOURCES, rsc->xml, cib_sync_call);
+ rc = pcmk_legacy2rc(rc);
+ if (rc == pcmk_rc_ok) {
+ out->info(out, "Set attribute: " PCMK_XA_NAME "=%s value=%s",
+ attr_name, attr_value);
}
- if (pcmk__str_eq(attr_set_type, XML_TAG_ATTR_SETS, pcmk__str_casei)) {
+ return rc;
+}
+
+static int
+resources_with_attr(pcmk__output_t *out, cib_t *cib, pcmk_resource_t *rsc,
+ const char *requested_name, const char *attr_set,
+ const char *attr_set_type, const char *attr_id,
+ const char *attr_name, const char *top_id, gboolean force,
+ GList **resources)
+{
+ if (pcmk__str_eq(attr_set_type, PCMK_XE_INSTANCE_ATTRIBUTES,
+ pcmk__str_casei)) {
if (!force) {
- rc = find_resource_attr(out, cib, XML_ATTR_ID, top_id,
- XML_TAG_META_SETS, attr_set, attr_id,
- attr_name, &found_attr_id);
- if ((rc == pcmk_rc_ok) && !out->is_quiet(out)) {
- out->err(out,
- "WARNING: There is already a meta attribute "
- "for '%s' called '%s' (id=%s)",
- top_id, attr_name, found_attr_id);
- out->err(out,
- " Delete '%s' first or use the force option "
- "to override", found_attr_id);
- }
- free(found_attr_id);
- if (rc == pcmk_rc_ok) {
+ xmlNode *xml_search = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = find_resource_attr(out, cib, PCMK_XA_ID, top_id,
+ PCMK_XE_META_ATTRIBUTES, attr_set, attr_id,
+ attr_name, &xml_search);
+
+ if (rc == pcmk_rc_ok || rc == ENOTUNIQ) {
+ char *found_attr_id = NULL;
+
+ found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID);
+
+ if (!out->is_quiet(out)) {
+ out->err(out,
+ "WARNING: There is already a meta attribute "
+ "for '%s' called '%s' (id=%s)",
+ top_id, attr_name, found_attr_id);
+ out->err(out,
+ " Delete '%s' first or use the force option "
+ "to override", found_attr_id);
+ }
+
+ free(found_attr_id);
+ free_xml(xml_search);
return ENOTUNIQ;
}
- }
- resources = g_list_append(resources, rsc);
- } else if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) {
- crm_xml_add(rsc->xml, attr_name, attr_value);
- CRM_ASSERT(cib != NULL);
- rc = cib->cmds->replace(cib, XML_CIB_TAG_RESOURCES, rsc->xml,
- cib_options);
- rc = pcmk_legacy2rc(rc);
- if (rc == pcmk_rc_ok) {
- out->info(out, "Set attribute: name=%s value=%s",
- attr_name, attr_value);
+ free_xml(xml_search);
}
- return rc;
+
+ *resources = g_list_append(*resources, rsc);
} else {
- resources = find_matching_attr_resources(out, rsc, requested_name,
- attr_set, attr_set_type,
- attr_id, attr_name, cib,
- "update", force);
+ *resources = find_matching_attr_resources(out, rsc, requested_name,
+ attr_set, attr_set_type,
+ attr_id, attr_name, cib,
+ "update", force);
}
/* If the user specified attr_set or attr_id, the intent is to modify a
* single resource, which will be the last item in the list.
*/
if ((attr_set != NULL) || (attr_id != NULL)) {
- GList *last = g_list_last(resources);
+ GList *last = g_list_last(*resources);
+
+ *resources = g_list_remove_link(*resources, last);
+ g_list_free(*resources);
+ *resources = last;
+ }
+
+ return pcmk_rc_ok;
+}
+
+static void
+free_attr_update_data(gpointer data)
+{
+ attr_update_data_t *ud = data;
+
+ if (ud == NULL) {
+ return;
+ }
+
+ free(ud->attr_set_type);
+ free(ud->attr_set_id);
+ free(ud->attr_name);
+ free(ud->attr_value);
+ free(ud->given_rsc_id);
+ free(ud->found_attr_id);
+ free(ud);
+}
+
+static int
+update_attribute(pcmk_resource_t *rsc, const char *requested_name,
+ const char *attr_set, const char *attr_set_type,
+ const char *attr_id, const char *attr_name,
+ const char *attr_value, gboolean recursive, cib_t *cib,
+ gboolean force, GList **results)
+{
+ pcmk__output_t *out = rsc->cluster->priv;
+ int rc = pcmk_rc_ok;
+
+ GList/*<pcmk_resource_t*>*/ *resources = NULL;
+ const char *top_id = pe__const_top_resource(rsc, false)->id;
+
+ if ((attr_id == NULL) && !force) {
+ find_resource_attr(out, cib, PCMK_XA_ID, top_id, NULL, NULL, NULL,
+ attr_name, NULL);
+ }
- resources = g_list_remove_link(resources, last);
- g_list_free(resources);
- resources = last;
+ rc = resources_with_attr(out, cib, rsc, requested_name, attr_set, attr_set_type,
+ attr_id, attr_name, top_id, force, &resources);
+
+ if (rc != pcmk_rc_ok) {
+ return rc;
}
for (GList *iter = resources; iter != NULL; iter = iter->next) {
char *lookup_id = NULL;
char *local_attr_set = NULL;
+ char *found_attr_id = NULL;
const char *rsc_attr_id = attr_id;
const char *rsc_attr_set = attr_set;
xmlNode *xml_top = NULL;
xmlNode *xml_obj = NULL;
- found_attr_id = NULL;
+ xmlNode *xml_search = NULL;
rsc = (pcmk_resource_t *) iter->data;
lookup_id = clone_strip(rsc->id); /* Could be a cloned group! */
- rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type,
- attr_set, attr_id, attr_name, &found_attr_id);
+ rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type,
+ attr_set, attr_id, attr_name, &xml_search);
switch (rc) {
case pcmk_rc_ok:
- crm_debug("Found a match for name=%s: id=%s",
- attr_name, found_attr_id);
+ found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID);
+ crm_debug("Found a match for " PCMK_XA_NAME "='%s': "
+ PCMK_XA_ID "='%s'", attr_name, found_attr_id);
rsc_attr_id = found_attr_id;
break;
@@ -363,16 +409,17 @@ cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name,
rsc_attr_id = found_attr_id;
}
- xml_top = create_xml_node(NULL, (const char *) rsc->xml->name);
- crm_xml_add(xml_top, XML_ATTR_ID, lookup_id);
+ xml_top = pcmk__xe_create(NULL, (const char *) rsc->xml->name);
+ crm_xml_add(xml_top, PCMK_XA_ID, lookup_id);
- xml_obj = create_xml_node(xml_top, attr_set_type);
- crm_xml_add(xml_obj, XML_ATTR_ID, rsc_attr_set);
+ xml_obj = pcmk__xe_create(xml_top, attr_set_type);
+ crm_xml_add(xml_obj, PCMK_XA_ID, rsc_attr_set);
break;
default:
free(lookup_id);
free(found_attr_id);
+ free_xml(xml_search);
g_list_free(resources);
return rc;
}
@@ -385,66 +432,114 @@ cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name,
crm_log_xml_debug(xml_top, "Update");
- rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top,
- cib_options);
+ rc = cib->cmds->modify(cib, PCMK_XE_RESOURCES, xml_top, cib_sync_call);
rc = pcmk_legacy2rc(rc);
if (rc == pcmk_rc_ok) {
- out->info(out, "Set '%s' option: id=%s%s%s%s%s value=%s",
- lookup_id, found_attr_id,
- ((rsc_attr_set == NULL)? "" : " set="),
- pcmk__s(rsc_attr_set, ""),
- ((attr_name == NULL)? "" : " name="),
- pcmk__s(attr_name, ""), attr_value);
+ attr_update_data_t *ud = pcmk__assert_alloc(1, sizeof(attr_update_data_t));
+
+ if (attr_set_type == NULL) {
+ attr_set_type = (const char *) xml_search->parent->name;
+ }
+
+ if (rsc_attr_set == NULL) {
+ rsc_attr_set = crm_element_value(xml_search->parent, PCMK_XA_ID);
+ }
+
+ ud->attr_set_type = pcmk__str_copy(attr_set_type);
+ ud->attr_set_id = pcmk__str_copy(rsc_attr_set);
+ ud->attr_name = pcmk__str_copy(attr_name);
+ ud->attr_value = pcmk__str_copy(attr_value);
+ ud->given_rsc_id = pcmk__str_copy(lookup_id);
+ ud->found_attr_id = pcmk__str_copy(found_attr_id);
+ ud->rsc = rsc;
+
+ *results = g_list_append(*results, ud);
}
free_xml(xml_top);
+ free_xml(xml_search);
free(lookup_id);
free(found_attr_id);
free(local_attr_set);
if (recursive
- && pcmk__str_eq(attr_set_type, XML_TAG_META_SETS,
+ && pcmk__str_eq(attr_set_type, PCMK_XE_META_ATTRIBUTES,
pcmk__str_casei)) {
- GList *lpc = NULL;
- static bool need_init = true;
-
- if (need_init) {
- need_init = false;
- pcmk__unpack_constraints(rsc->cluster);
- pe__clear_resource_flags_on_all(rsc->cluster,
- pcmk_rsc_detect_loop);
- }
-
/* We want to set the attribute only on resources explicitly
* colocated with this one, so we use rsc->rsc_cons_lhs directly
* rather than the with_this_colocations() method.
*/
- pe__set_resource_flags(rsc, pcmk_rsc_detect_loop);
- for (lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
+ pcmk__set_rsc_flags(rsc, pcmk_rsc_detect_loop);
+ for (GList *lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) {
pcmk__colocation_t *cons = (pcmk__colocation_t *) lpc->data;
crm_debug("Checking %s %d", cons->id, cons->score);
- if (!pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)
- && (cons->score > 0)) {
- crm_debug("Setting %s=%s for dependent resource %s",
- attr_name, attr_value, cons->dependent->id);
- cli_resource_update_attribute(cons->dependent,
- cons->dependent->id, NULL,
- attr_set_type, NULL,
- attr_name, attr_value,
- recursive, cib, cib_options,
- force);
+
+ if (pcmk_is_set(cons->dependent->flags, pcmk_rsc_detect_loop)
+ || (cons->score <= 0)) {
+ continue;
}
+
+ crm_debug("Setting %s=%s for dependent resource %s",
+ attr_name, attr_value, cons->dependent->id);
+ update_attribute(cons->dependent, cons->dependent->id, NULL,
+ attr_set_type, NULL, attr_name, attr_value,
+ recursive, cib, force, results);
}
}
}
+
g_list_free(resources);
return rc;
}
// \return Standard Pacemaker return code
int
+cli_resource_update_attribute(pcmk_resource_t *rsc, const char *requested_name,
+ const char *attr_set, const char *attr_set_type,
+ const char *attr_id, const char *attr_name,
+ const char *attr_value, gboolean recursive,
+ cib_t *cib, gboolean force)
+{
+ static bool need_init = true;
+ int rc = pcmk_rc_ok;
+
+ GList *results = NULL;
+ pcmk__output_t *out = rsc->cluster->priv;
+
+ /* If we were asked to update the attribute in a resource element (for
+ * instance, <primitive class="ocf">) there's really not much we need to do.
+ */
+ if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) {
+ return update_element_attribute(out, rsc, cib, attr_name, attr_value);
+ }
+
+ /* One time initialization - clear flags so we can detect loops */
+ if (need_init) {
+ need_init = false;
+ pcmk__unpack_constraints(rsc->cluster);
+ pe__clear_resource_flags_on_all(rsc->cluster, pcmk_rsc_detect_loop);
+ }
+
+ rc = update_attribute(rsc, requested_name, attr_set, attr_set_type,
+ attr_id, attr_name, attr_value, recursive, cib,
+ force, &results);
+
+ if (rc == pcmk_rc_ok) {
+ if (results == NULL) {
+ return rc;
+ }
+
+ out->message(out, "attribute-changed-list", results);
+ g_list_free_full(results, free_attr_update_data);
+ }
+
+ return rc;
+}
+
+// \return Standard Pacemaker return code
+int
cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name,
const char *attr_set, const char *attr_set_type,
const char *attr_id, const char *attr_name,
@@ -455,22 +550,21 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name,
GList/*<pcmk_resource_t*>*/ *resources = NULL;
if ((attr_id == NULL) && !force) {
- find_resource_attr(out, cib, XML_ATTR_ID,
+ find_resource_attr(out, cib, PCMK_XA_ID,
pe__const_top_resource(rsc, false)->id, NULL,
NULL, NULL, attr_name, NULL);
}
- if (pcmk__str_eq(attr_set_type, XML_TAG_META_SETS, pcmk__str_casei)) {
+ if (pcmk__str_eq(attr_set_type, PCMK_XE_META_ATTRIBUTES, pcmk__str_casei)) {
resources = find_matching_attr_resources(out, rsc, requested_name,
attr_set, attr_set_type,
attr_id, attr_name, cib,
"delete", force);
} else if (pcmk__str_eq(attr_set_type, ATTR_SET_ELEMENT, pcmk__str_none)) {
- xml_remove_prop(rsc->xml, attr_name);
+ pcmk__xe_remove_attr(rsc->xml, attr_name);
CRM_ASSERT(cib != NULL);
- rc = cib->cmds->replace(cib, XML_CIB_TAG_RESOURCES, rsc->xml,
- cib_options);
+ rc = cib->cmds->replace(cib, PCMK_XE_RESOURCES, rsc->xml, cib_options);
rc = pcmk_legacy2rc(rc);
if (rc == pcmk_rc_ok) {
out->info(out, "Deleted attribute: %s", attr_name);
@@ -484,25 +578,29 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name,
for (GList *iter = resources; iter != NULL; iter = iter->next) {
char *lookup_id = NULL;
xmlNode *xml_obj = NULL;
+ xmlNode *xml_search = NULL;
char *found_attr_id = NULL;
const char *rsc_attr_id = attr_id;
rsc = (pcmk_resource_t *) iter->data;
lookup_id = clone_strip(rsc->id);
- rc = find_resource_attr(out, cib, XML_ATTR_ID, lookup_id, attr_set_type,
- attr_set, attr_id, attr_name, &found_attr_id);
+ rc = find_resource_attr(out, cib, PCMK_XA_ID, lookup_id, attr_set_type,
+ attr_set, attr_id, attr_name, &xml_search);
switch (rc) {
case pcmk_rc_ok:
+ found_attr_id = crm_element_value_copy(xml_search, PCMK_XA_ID);
+ free_xml(xml_search);
break;
case ENXIO:
free(lookup_id);
- rc = pcmk_rc_ok;
+ free_xml(xml_search);
continue;
default:
free(lookup_id);
+ free_xml(xml_search);
g_list_free(resources);
return rc;
}
@@ -515,16 +613,15 @@ cli_resource_delete_attribute(pcmk_resource_t *rsc, const char *requested_name,
crm_log_xml_debug(xml_obj, "Delete");
CRM_ASSERT(cib);
- rc = cib->cmds->remove(cib, XML_CIB_TAG_RESOURCES, xml_obj,
- cib_options);
+ rc = cib->cmds->remove(cib, PCMK_XE_RESOURCES, xml_obj, cib_options);
rc = pcmk_legacy2rc(rc);
if (rc == pcmk_rc_ok) {
- out->info(out, "Deleted '%s' option: id=%s%s%s%s%s",
+ out->info(out, "Deleted '%s' option: " PCMK_XA_ID "=%s%s%s%s%s",
lookup_id, found_attr_id,
((attr_set == NULL)? "" : " set="),
pcmk__s(attr_set, ""),
- ((attr_name == NULL)? "" : " name="),
+ ((attr_name == NULL)? "" : " " PCMK_XA_NAME "="),
pcmk__s(attr_name, ""));
}
@@ -556,21 +653,21 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource,
out->err(out, "Resource %s not found", rsc_id);
return ENXIO;
- } else if (rsc->variant != pcmk_rsc_variant_primitive) {
+ } else if (!pcmk__is_primitive(rsc)) {
out->err(out, "We can only process primitive resources, not %s", rsc_id);
return EINVAL;
}
- rsc_class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
- rsc_provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER),
- rsc_type = crm_element_value(rsc->xml, XML_ATTR_TYPE);
+ rsc_class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
+ rsc_provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER),
+ rsc_type = crm_element_value(rsc->xml, PCMK_XA_TYPE);
if ((rsc_class == NULL) || (rsc_type == NULL)) {
out->err(out, "Resource %s does not have a class and type", rsc_id);
return EINVAL;
}
{
- pcmk_node_t *node = pe_find_node(scheduler->nodes, host_uname);
+ pcmk_node_t *node = pcmk_find_node(scheduler, host_uname);
if (node == NULL) {
out->err(out, "Node %s not found", host_uname);
@@ -585,8 +682,8 @@ send_lrm_rsc_op(pcmk_ipc_api_t *controld_api, bool do_fail_resource,
cib_only = true;
}
}
- if (!cib_only && pe__is_guest_or_remote_node(node)) {
- node = pe__current_node(node->details->remote_rsc);
+ if (!cib_only && pcmk__is_pacemaker_remote_node(node)) {
+ node = pcmk__current_node(node->details->remote_rsc);
if (node == NULL) {
out->err(out, "No cluster connection to Pacemaker Remote node %s detected",
host_uname);
@@ -668,7 +765,7 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api,
int rc = pcmk_rc_ok;
const char *failed_value = NULL;
const char *failed_id = NULL;
- const char *interval_ms_s = NULL;
+ char *interval_ms_s = NULL;
GHashTable *rscs = NULL;
GHashTableIter iter;
@@ -680,15 +777,17 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api,
// Normalize interval to milliseconds for comparison to history entry
if (operation) {
- interval_ms_s = crm_strdup_printf("%u",
- crm_parse_interval_spec(interval_spec));
+ guint interval_ms = 0U;
+
+ pcmk_parse_interval_spec(interval_spec, &interval_ms);
+ interval_ms_s = crm_strdup_printf("%u", interval_ms);
}
- for (xmlNode *xml_op = pcmk__xml_first_child(scheduler->failed);
- xml_op != NULL;
- xml_op = pcmk__xml_next(xml_op)) {
+ for (xmlNode *xml_op = pcmk__xe_first_child(scheduler->failed, NULL, NULL,
+ NULL);
+ xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) {
- failed_id = crm_element_value(xml_op, XML_LRM_ATTR_RSCID);
+ failed_id = crm_element_value(xml_op, PCMK__XA_RSC_ID);
if (failed_id == NULL) {
// Malformed history entry, should never happen
continue;
@@ -708,20 +807,20 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api,
}
// Host name should always have been provided by this point
- failed_value = crm_element_value(xml_op, XML_ATTR_UNAME);
+ failed_value = crm_element_value(xml_op, PCMK_XA_UNAME);
if (!pcmk__str_eq(node_name, failed_value, pcmk__str_casei)) {
continue;
}
// No operation specified means all operations match
if (operation) {
- failed_value = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
+ failed_value = crm_element_value(xml_op, PCMK_XA_OPERATION);
if (!pcmk__str_eq(operation, failed_value, pcmk__str_casei)) {
continue;
}
// Interval (if operation was specified) defaults to 0 (not all)
- failed_value = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS);
+ failed_value = crm_element_value(xml_op, PCMK_META_INTERVAL);
if (!pcmk__str_eq(interval_ms_s, failed_value, pcmk__str_casei)) {
continue;
}
@@ -730,6 +829,8 @@ clear_rsc_failures(pcmk__output_t *out, pcmk_ipc_api_t *controld_api,
g_hash_table_add(rscs, (gpointer) failed_id);
}
+ free(interval_ms_s);
+
g_hash_table_iter_init(&iter, rscs);
while (g_hash_table_iter_next(&iter, (gpointer *) &failed_id, NULL)) {
crm_debug("Erasing failures of %s on %s", failed_id, node_name);
@@ -751,7 +852,7 @@ clear_rsc_fail_attrs(const pcmk_resource_t *rsc, const char *operation,
int attr_options = pcmk__node_attr_none;
char *rsc_name = rsc_fail_name(rsc);
- if (pe__is_guest_or_remote_node(node)) {
+ if (pcmk__is_pacemaker_remote_node(node)) {
attr_options |= pcmk__node_attr_remote;
}
@@ -830,7 +931,7 @@ cli_resource_delete(pcmk_ipc_api_t *controld_api, const char *host_uname,
return pcmk_rc_ok;
}
- node = pe_find_node(scheduler->nodes, host_uname);
+ node = pcmk_find_node(scheduler, host_uname);
if (node == NULL) {
out->err(out, "Unable to clean up %s because node %s not found",
@@ -890,13 +991,13 @@ cli_cleanup_all(pcmk_ipc_api_t *controld_api, const char *node_name,
}
if (node_name) {
- pcmk_node_t *node = pe_find_node(scheduler->nodes, node_name);
+ pcmk_node_t *node = pcmk_find_node(scheduler, node_name);
if (node == NULL) {
out->err(out, "Unknown node: %s", node_name);
return ENXIO;
}
- if (pe__is_guest_or_remote_node(node)) {
+ if (pcmk__is_pacemaker_remote_node(node)) {
attr_options |= pcmk__node_attr_remote;
}
}
@@ -939,12 +1040,12 @@ static void
check_role(resource_checks_t *checks)
{
const char *role_s = g_hash_table_lookup(checks->rsc->meta,
- XML_RSC_ATTR_TARGET_ROLE);
+ PCMK_META_TARGET_ROLE);
if (role_s == NULL) {
return;
}
- switch (text2role(role_s)) {
+ switch (pcmk_parse_role(role_s)) {
case pcmk_role_stopped:
checks->flags |= rsc_remain_stopped;
break;
@@ -965,7 +1066,7 @@ static void
check_managed(resource_checks_t *checks)
{
const char *managed_s = g_hash_table_lookup(checks->rsc->meta,
- XML_RSC_ATTR_MANAGED);
+ PCMK_META_IS_MANAGED);
if ((managed_s != NULL) && !crm_is_true(managed_s)) {
checks->flags |= rsc_unmanaged;
@@ -1075,12 +1176,12 @@ generate_resource_params(pcmk_resource_t *rsc, pcmk_node_t *node,
if (params != NULL) {
g_hash_table_iter_init(&iter, params);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
- g_hash_table_insert(combined, strdup(key), strdup(value));
+ pcmk__insert_dup(combined, key, value);
}
}
meta = pcmk__strkey_table(free, free);
- get_meta_attributes(meta, rsc, node, scheduler);
+ get_meta_attributes(meta, rsc, NULL, scheduler);
if (meta != NULL) {
g_hash_table_iter_init(&iter, meta);
while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
@@ -1150,7 +1251,7 @@ get_active_resources(const char *host, GList *rsc_list)
* other than the first, we can't otherwise tell which resources are
* stopping and starting.
*/
- if (rsc->variant == pcmk_rsc_variant_group) {
+ if (pcmk__is_group(rsc)) {
active = g_list_concat(active,
get_active_resources(host, rsc->children));
} else if (resource_is_running_on(rsc, host)) {
@@ -1192,18 +1293,16 @@ static void display_list(pcmk__output_t *out, GList *items, const char *tag)
* \return Standard Pacemaker return code
* \note On success, caller is responsible for freeing memory allocated for
* scheduler->now.
- * \todo This follows the example of other callers of cli_config_update()
- * and returns ENOKEY ("Required key not available") if that fails,
- * but perhaps pcmk_rc_schema_validation would be better in that case.
*/
int
update_scheduler_input(pcmk_scheduler_t *scheduler, xmlNode **xml)
{
- if (cli_config_update(xml, NULL, FALSE) == FALSE) {
- return ENOKEY;
+ int rc = pcmk_update_configured_schema(xml, false);
+
+ if (rc == pcmk_rc_ok) {
+ scheduler->input = *xml;
+ scheduler->now = crm_time_new(NULL);
}
- scheduler->input = *xml;
- scheduler->now = crm_time_new(NULL);
return pcmk_rc_ok;
}
@@ -1254,7 +1353,7 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate)
pcmk__output_t *out = scheduler->priv;
pe_reset_working_set(scheduler);
- pe__set_working_set_flags(scheduler,
+ pcmk__set_scheduler_flags(scheduler,
pcmk_sched_no_counts|pcmk_sched_no_compat);
rc = update_scheduler_input_to_cib(out, scheduler, cib);
if (rc != pcmk_rc_ok) {
@@ -1274,10 +1373,9 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate)
goto done;
}
- rc = write_xml_file(scheduler->input, shadow_file, FALSE);
-
- if (rc < 0) {
- out->err(out, "Could not populate shadow cib: %s (%d)", pcmk_strerror(rc), rc);
+ rc = pcmk__xml_write_file(scheduler->input, shadow_file, false, NULL);
+ if (rc != pcmk_rc_ok) {
+ out->err(out, "Could not populate shadow cib: %s", pcmk_rc_str(rc));
goto done;
}
@@ -1285,7 +1383,8 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate)
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
- out->err(out, "Could not connect to shadow cib: %s (%d)", pcmk_rc_str(rc), rc);
+ out->err(out, "Could not connect to shadow cib: %s",
+ pcmk_rc_str(rc));
goto done;
}
@@ -1325,11 +1424,11 @@ update_dataset(cib_t *cib, pcmk_scheduler_t *scheduler, bool simulate)
*
* \return Maximum stop timeout for \p rsc (in milliseconds)
*/
-static int
+static guint
max_rsc_stop_timeout(pcmk_resource_t *rsc)
{
long long result_ll;
- int max_delay = 0;
+ guint max_delay = 0;
xmlNode *config = NULL;
GHashTable *meta = NULL;
@@ -1341,12 +1440,13 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc)
if (rsc->children != NULL) {
for (GList *iter = rsc->children; iter; iter = iter->next) {
pcmk_resource_t *child = iter->data;
- int delay = max_rsc_stop_timeout(child);
+ guint delay = max_rsc_stop_timeout(child);
if (delay > max_delay) {
- pe_rsc_trace(rsc,
- "Maximum stop timeout for %s is now %s due to %s",
- rsc->id, pcmk__readable_interval(delay), child->id);
+ pcmk__rsc_trace(rsc,
+ "Maximum stop timeout for %s is now %s "
+ "due to %s", rsc->id,
+ pcmk__readable_interval(delay), child->id);
max_delay = delay;
}
}
@@ -1362,10 +1462,9 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc)
* @TODO This currently ignores node (which might matter for rules)
*/
meta = pcmk__unpack_action_meta(rsc, NULL, PCMK_ACTION_STOP, 0, config);
- if ((pcmk__scan_ll(g_hash_table_lookup(meta, XML_ATTR_TIMEOUT),
- &result_ll, -1LL) == pcmk_rc_ok)
- && (result_ll >= 0) && (result_ll <= INT_MAX)) {
- max_delay = (int) result_ll;
+ if ((pcmk__scan_ll(g_hash_table_lookup(meta, PCMK_META_TIMEOUT),
+ &result_ll, -1LL) == pcmk_rc_ok) && (result_ll >= 0)) {
+ max_delay = (guint) QB_MIN(result_ll, UINT_MAX);
}
g_hash_table_destroy(meta);
@@ -1387,26 +1486,26 @@ max_rsc_stop_timeout(pcmk_resource_t *rsc)
* throttling, or any demotions needed. It checks the stop timeout, even
* if the resources in question are actually being started.
*/
-static int
+static guint
wait_time_estimate(pcmk_scheduler_t *scheduler, const GList *resources)
{
- int max_delay = 0;
+ guint max_delay = 0U;
// Find maximum stop timeout in milliseconds
for (const GList *item = resources; item != NULL; item = item->next) {
pcmk_resource_t *rsc = pe_find_resource(scheduler->resources,
(const char *) item->data);
- int delay = max_rsc_stop_timeout(rsc);
+ guint delay = max_rsc_stop_timeout(rsc);
if (delay > max_delay) {
- pe_rsc_trace(rsc,
- "Wait time is now %s due to %s",
- pcmk__readable_interval(delay), rsc->id);
+ pcmk__rsc_trace(rsc,
+ "Wait time is now %s due to %s",
+ pcmk__readable_interval(delay), rsc->id);
max_delay = delay;
}
}
- return (max_delay / 1000) + 5;
+ return (max_delay / 1000U) + 5U;
}
#define waiting_for_starts(d, r, h) ((d != NULL) || \
@@ -1438,15 +1537,15 @@ wait_time_estimate(pcmk_scheduler_t *scheduler, const GList *resources)
int
cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
const pcmk_node_t *node, const char *move_lifetime,
- int timeout_ms, cib_t *cib, int cib_options,
+ guint timeout_ms, cib_t *cib, int cib_options,
gboolean promoted_role_only, gboolean force)
{
int rc = pcmk_rc_ok;
int lpc = 0;
int before = 0;
- int step_timeout_s = 0;
- int sleep_interval = 2;
- int timeout = timeout_ms / 1000;
+ guint step_timeout_s = 0;
+ guint sleep_interval = 2U;
+ guint timeout = timeout_ms / 1000U;
bool stop_via_ban = false;
char *rsc_id = NULL;
@@ -1468,14 +1567,14 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
/* If the implicit resource or primitive resource of a bundle is given, operate on the
* bundle itself instead.
*/
- if (pe_rsc_is_bundled(rsc)) {
+ if (pcmk__is_bundled(rsc)) {
rsc = parent->parent;
}
running = resource_is_running_on(rsc, host);
- if (pe_rsc_is_clone(parent) && !running) {
- if (pe_rsc_is_unique_clone(parent)) {
+ if (pcmk__is_clone(parent) && !running) {
+ if (pcmk__is_unique_clone(parent)) {
lookup_id = strdup(rsc->id);
} else {
lookup_id = clone_strip(rsc->id);
@@ -1504,16 +1603,16 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
rsc_id = strdup(rsc->id);
- if (pe_rsc_is_unique_clone(parent)) {
+ if (pcmk__is_unique_clone(parent)) {
lookup_id = strdup(rsc->id);
} else {
lookup_id = clone_strip(rsc->id);
}
if (host) {
- if (pe_rsc_is_clone(rsc) || pe_bundle_replicas(rsc)) {
+ if (pcmk__is_clone(rsc) || pe_bundle_replicas(rsc)) {
stop_via_ban = true;
- } else if (pe_rsc_is_clone(parent)) {
+ } else if (pcmk__is_clone(parent)) {
stop_via_ban = true;
free(lookup_id);
lookup_id = strdup(parent->id);
@@ -1563,22 +1662,33 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
out->quiet = true;
rc = cli_resource_ban(out, lookup_id, host, move_lifetime, cib,
cib_options, promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ PCMK_ROLE_PROMOTED);
} else {
- /* Stop the resource by setting target-role to Stopped.
- * Remember any existing target-role so we can restore it later
- * (though it only makes any difference if it's Unpromoted).
+ xmlNode *xml_search = NULL;
+
+ /* Stop the resource by setting PCMK_META_TARGET_ROLE to Stopped.
+ * Remember any existing PCMK_META_TARGET_ROLE so we can restore it
+ * later (though it only makes any difference if it's Unpromoted).
*/
- find_resource_attr(out, cib, XML_NVPAIR_ATTR_VALUE, lookup_id, NULL, NULL,
- NULL, XML_RSC_ATTR_TARGET_ROLE, &orig_target_role);
- rc = cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS,
- NULL, XML_RSC_ATTR_TARGET_ROLE,
+ rc = find_resource_attr(out, cib, PCMK_XA_VALUE, lookup_id, NULL, NULL, NULL,
+ PCMK_META_TARGET_ROLE, &xml_search);
+
+ if (rc == pcmk_rc_ok) {
+ orig_target_role = crm_element_value_copy(xml_search, PCMK_XA_VALUE);
+ }
+
+ free_xml(xml_search);
+
+ rc = cli_resource_update_attribute(rsc, rsc_id, NULL,
+ PCMK_XE_META_ATTRIBUTES, NULL,
+ PCMK_META_TARGET_ROLE,
PCMK_ACTION_STOPPED, FALSE, cib,
- cib_options, force);
+ force);
}
if(rc != pcmk_rc_ok) {
- out->err(out, "Could not set target-role for %s: %s (%d)", rsc_id, pcmk_rc_str(rc), rc);
+ out->err(out, "Could not set " PCMK_META_TARGET_ROLE " for %s: %s (%d)",
+ rsc_id, pcmk_rc_str(rc), rc);
if (current_active != NULL) {
g_list_free_full(current_active, free);
current_active = NULL;
@@ -1616,7 +1726,7 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
sleep(sleep_interval);
if(timeout) {
timeout -= sleep_interval;
- crm_trace("%ds remaining", timeout);
+ crm_trace("%us remaining", timeout);
}
rc = update_dataset(cib, scheduler, FALSE);
if(rc != pcmk_rc_ok) {
@@ -1651,20 +1761,23 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
rc = cli_resource_clear(lookup_id, host, NULL, cib, cib_options, true, force);
} else if (orig_target_role) {
- rc = cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS,
- NULL, XML_RSC_ATTR_TARGET_ROLE,
- orig_target_role, FALSE, cib,
- cib_options, force);
+ rc = cli_resource_update_attribute(rsc, rsc_id, NULL,
+ PCMK_XE_META_ATTRIBUTES, NULL,
+ PCMK_META_TARGET_ROLE,
+ orig_target_role, FALSE, cib, force);
free(orig_target_role);
orig_target_role = NULL;
} else {
- rc = cli_resource_delete_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS,
- NULL, XML_RSC_ATTR_TARGET_ROLE, cib,
+ rc = cli_resource_delete_attribute(rsc, rsc_id, NULL,
+ PCMK_XE_META_ATTRIBUTES, NULL,
+ PCMK_META_TARGET_ROLE, cib,
cib_options, force);
}
if(rc != pcmk_rc_ok) {
- out->err(out, "Could not unset target-role for %s: %s (%d)", rsc_id, pcmk_rc_str(rc), rc);
+ out->err(out,
+ "Could not unset " PCMK_META_TARGET_ROLE " for %s: %s (%d)",
+ rsc_id, pcmk_rc_str(rc), rc);
goto done;
}
@@ -1731,14 +1844,16 @@ cli_resource_restart(pcmk__output_t *out, pcmk_resource_t *rsc,
if (stop_via_ban) {
cli_resource_clear(lookup_id, host, NULL, cib, cib_options, true, force);
} else if (orig_target_role) {
- cli_resource_update_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS, NULL,
- XML_RSC_ATTR_TARGET_ROLE, orig_target_role,
- FALSE, cib, cib_options, force);
+ cli_resource_update_attribute(rsc, rsc_id, NULL,
+ PCMK_XE_META_ATTRIBUTES, NULL,
+ PCMK_META_TARGET_ROLE, orig_target_role,
+ FALSE, cib, force);
free(orig_target_role);
} else {
- cli_resource_delete_attribute(rsc, rsc_id, NULL, XML_TAG_META_SETS,
- NULL, XML_RSC_ATTR_TARGET_ROLE, cib,
- cib_options, force);
+ cli_resource_delete_attribute(rsc, rsc_id, NULL,
+ PCMK_XE_META_ATTRIBUTES, NULL,
+ PCMK_META_TARGET_ROLE, cib, cib_options,
+ force);
}
done:
@@ -1809,7 +1924,7 @@ print_pending_actions(pcmk__output_t *out, GList *actions)
if (a->node) {
out->info(out, "\tAction %d: %s\ton %s",
- a->id, a->uuid, pe__node_name(a->node));
+ a->id, a->uuid, pcmk__node_name(a->node));
} else {
out->info(out, "\tAction %d: %s", a->id, a->uuid);
}
@@ -1839,32 +1954,47 @@ print_pending_actions(pcmk__output_t *out, GList *actions)
* \return Standard Pacemaker return code
*/
int
-wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib)
+wait_till_stable(pcmk__output_t *out, guint timeout_ms, cib_t * cib)
{
pcmk_scheduler_t *scheduler = NULL;
xmlXPathObjectPtr search;
int rc = pcmk_rc_ok;
bool pending_unknown_state_resources;
- int timeout_s = timeout_ms? ((timeout_ms + 999) / 1000) : WAIT_DEFAULT_TIMEOUT_S;
- time_t expire_time = time(NULL) + timeout_s;
+ time_t expire_time = time(NULL);
time_t time_diff;
bool printed_version_warning = out->is_quiet(out); // i.e. don't print if quiet
+ char *xpath = NULL;
+
+ if (timeout_ms == 0) {
+ expire_time += WAIT_DEFAULT_TIMEOUT_S;
+ } else {
+ expire_time += (timeout_ms + 999) / 1000;
+ }
scheduler = pe_new_working_set();
if (scheduler == NULL) {
return ENOMEM;
}
+ xpath = crm_strdup_printf("/" PCMK_XE_CIB "/" PCMK_XE_STATUS
+ "/" PCMK__XE_NODE_STATE "/" PCMK__XE_LRM
+ "/" PCMK__XE_LRM_RESOURCES
+ "/" PCMK__XE_LRM_RESOURCE
+ "/" PCMK__XE_LRM_RSC_OP
+ "[@" PCMK__XA_RC_CODE "='%d']",
+ PCMK_OCF_UNKNOWN);
do {
/* Abort if timeout is reached */
time_diff = expire_time - time(NULL);
- if (time_diff > 0) {
- crm_info("Waiting up to %lld seconds for cluster actions to complete", (long long) time_diff);
- } else {
+ if (time_diff <= 0) {
print_pending_actions(out, scheduler->actions);
- pe_free_working_set(scheduler);
- return ETIME;
+ rc = ETIME;
+ break;
}
+
+ crm_info("Waiting up to %lld seconds for cluster actions to complete",
+ (long long) time_diff);
+
if (rc == pcmk_rc_ok) { /* this avoids sleep on first loop iteration */
sleep(WAIT_SLEEP_S);
}
@@ -1873,8 +2003,7 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib)
pe_reset_working_set(scheduler);
rc = update_scheduler_input_to_cib(out, scheduler, cib);
if (rc != pcmk_rc_ok) {
- pe_free_working_set(scheduler);
- return rc;
+ break;
}
pcmk__schedule_actions(scheduler->input,
pcmk_sched_no_counts|pcmk_sched_no_compat,
@@ -1890,7 +2019,7 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib)
* DC. However, that would have potential problems of its own.
*/
const char *dc_version = g_hash_table_lookup(scheduler->config_hash,
- "dc-version");
+ PCMK_OPT_DC_VERSION);
if (!pcmk__str_eq(dc_version, PACEMAKER_VERSION "-" BUILD_VERSION, pcmk__str_casei)) {
out->info(out, "warning: wait option may not work properly in "
@@ -1899,13 +2028,13 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib)
}
}
- search = xpath_search(scheduler->input, "/cib/status/node_state/lrm/lrm_resources/lrm_resource/"
- XML_LRM_TAG_RSC_OP "[@" XML_LRM_ATTR_RC "='193']");
+ search = xpath_search(scheduler->input, xpath);
pending_unknown_state_resources = (numXpathResults(search) > 0);
freeXpathObject(search);
} while (actions_are_pending(scheduler->actions) || pending_unknown_state_resources);
pe_free_working_set(scheduler);
+ free(xpath);
return rc;
}
@@ -1943,14 +2072,13 @@ get_action(const char *rsc_action) {
* \param[in] verbosity Verbosity level
*/
static void
-set_agent_environment(GHashTable *params, int timeout_ms, int check_level,
+set_agent_environment(GHashTable *params, guint timeout_ms, int check_level,
int verbosity)
{
- g_hash_table_insert(params, strdup("CRM_meta_timeout"),
- crm_strdup_printf("%d", timeout_ms));
+ g_hash_table_insert(params, crm_meta_name(PCMK_META_TIMEOUT),
+ crm_strdup_printf("%u", timeout_ms));
- g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION),
- strdup(CRM_FEATURE_SET));
+ pcmk__insert_dup(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
if (check_level >= 0) {
char *level = crm_strdup_printf("%d", check_level);
@@ -1989,7 +2117,7 @@ apply_overrides(GHashTable *params, GHashTable *overrides)
g_hash_table_iter_init(&iter, overrides);
while (g_hash_table_iter_next(&iter, (gpointer *) &name,
(gpointer *) &value)) {
- g_hash_table_replace(params, strdup(name), strdup(value));
+ pcmk__insert_dup(params, name, value);
}
}
}
@@ -1999,8 +2127,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
const char *rsc_class, const char *rsc_prov,
const char *rsc_type, const char *rsc_action,
GHashTable *params, GHashTable *override_hash,
- int timeout_ms, int resource_verbose, gboolean force,
- int check_level)
+ guint timeout_ms, int resource_verbose,
+ gboolean force, int check_level)
{
const char *class = rsc_class;
const char *action = get_action(rsc_action);
@@ -2008,7 +2136,7 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
svc_action_t *op = NULL;
// If no timeout was provided, use the same default as the cluster
- if (timeout_ms == 0) {
+ if (timeout_ms == 0U) {
timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
}
@@ -2017,7 +2145,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
op = services__create_resource_action(rsc_name? rsc_name : "test",
rsc_class, rsc_prov, rsc_type, action,
- 0, timeout_ms, params, 0);
+ 0, QB_MIN(timeout_ms, INT_MAX),
+ params, 0);
if (op == NULL) {
out->err(out, "Could not execute %s using %s%s%s:%s: %s",
action, rsc_class, (rsc_prov? ":" : ""),
@@ -2060,10 +2189,33 @@ done:
return exit_code;
}
+/*!
+ * \internal
+ * \brief Get the timeout the cluster would use for an action
+ *
+ * \param[in] rsc Resource that action is for
+ * \param[in] action Name of action
+ */
+static guint
+get_action_timeout(pcmk_resource_t *rsc, const char *action)
+{
+ long long timeout_ms = -1LL;
+ xmlNode *op = pcmk__find_action_config(rsc, action, 0, true);
+ GHashTable *meta = pcmk__unpack_action_meta(rsc, NULL, action, 0, op);
+
+ if ((pcmk__scan_ll(g_hash_table_lookup(meta, PCMK_META_TIMEOUT),
+ &timeout_ms, -1LL) != pcmk_rc_ok)
+ || (timeout_ms <= 0LL)) {
+ timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
+ }
+ g_hash_table_destroy(meta);
+ return (guint) QB_MIN(timeout_ms, UINT_MAX);
+}
+
crm_exit_t
cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name,
const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t *cib, pcmk_scheduler_t *scheduler,
+ guint timeout_ms, cib_t *cib, pcmk_scheduler_t *scheduler,
int resource_verbose, gboolean force, int check_level)
{
pcmk__output_t *out = scheduler->priv;
@@ -2076,13 +2228,15 @@ cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name,
if (pcmk__strcase_any_of(rsc_action, "force-start", "force-demote",
"force-promote", NULL)) {
- if(pe_rsc_is_clone(rsc)) {
+ if (pcmk__is_clone(rsc)) {
GList *nodes = cli_resource_search(rsc, requested_name, scheduler);
if(nodes != NULL && force == FALSE) {
out->err(out, "It is not safe to %s %s here: the cluster claims it is already active",
rsc_action, rsc->id);
- out->err(out, "Try setting target-role=Stopped first or specifying "
- "the force option");
+ out->err(out,
+ "Try setting "
+ PCMK_META_TARGET_ROLE "=" PCMK_ROLE_STOPPED
+ " first or specifying the force option");
return CRM_EX_UNSAFE;
}
@@ -2090,32 +2244,31 @@ cli_resource_execute(pcmk_resource_t *rsc, const char *requested_name,
}
}
- if(pe_rsc_is_clone(rsc)) {
+ if (pcmk__is_clone(rsc)) {
/* Grab the first child resource in the hope it's not a group */
rsc = rsc->children->data;
}
- if (rsc->variant == pcmk_rsc_variant_group) {
+ if (pcmk__is_group(rsc)) {
out->err(out, "Sorry, the %s option doesn't support group resources", rsc_action);
return CRM_EX_UNIMPLEMENT_FEATURE;
- } else if (pe_rsc_is_bundled(rsc)) {
+ } else if (pcmk__is_bundled(rsc)) {
out->err(out, "Sorry, the %s option doesn't support bundled resources", rsc_action);
return CRM_EX_UNIMPLEMENT_FEATURE;
}
- rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
- rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
- rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
+ rclass = crm_element_value(rsc->xml, PCMK_XA_CLASS);
+ rprov = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
+ rtype = crm_element_value(rsc->xml, PCMK_XA_TYPE);
params = generate_resource_params(rsc, NULL /* @TODO use local node */,
scheduler);
- if (timeout_ms == 0) {
- timeout_ms = pe_get_configured_timeout(rsc, get_action(rsc_action),
- scheduler);
+ if (timeout_ms == 0U) {
+ timeout_ms = get_action_timeout(rsc, get_action(rsc_action));
}
- rid = pe_rsc_is_anon_clone(rsc->parent)? requested_name : rsc->id;
+ rid = pcmk__is_anonymous_clone(rsc->parent)? requested_name : rsc->id;
exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, rsc_action,
params, override_hash, timeout_ms,
@@ -2134,7 +2287,7 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
int rc = pcmk_rc_ok;
unsigned int count = 0;
pcmk_node_t *current = NULL;
- pcmk_node_t *dest = pe_find_node(scheduler->nodes, host_name);
+ pcmk_node_t *dest = pcmk_find_node(scheduler, host_name);
bool cur_is_dest = false;
if (dest == NULL) {
@@ -2170,7 +2323,7 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
if (child_role == pcmk_role_promoted) {
rsc = child;
- promoted_node = pe__current_node(child);
+ promoted_node = pcmk__current_node(child);
promoted_count++;
}
}
@@ -2182,19 +2335,19 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
}
if (count > 1) {
- if (pe_rsc_is_clone(rsc)) {
+ if (pcmk__is_clone(rsc)) {
current = NULL;
} else {
return pcmk_rc_multiple;
}
}
- if (current && (current->details == dest->details)) {
+ if (pcmk__same_node(current, dest)) {
cur_is_dest = true;
if (force) {
crm_info("%s is already %s on %s, reinforcing placement with location constraint.",
rsc_id, promoted_role_only?"promoted":"active",
- pe__node_name(dest));
+ pcmk__node_name(dest));
} else {
return pcmk_rc_already;
}
@@ -2211,11 +2364,11 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
/* Record an explicit preference for 'dest' */
rc = cli_resource_prefer(out, rsc_id, dest->details->uname, move_lifetime,
cib, cib_options, promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ PCMK_ROLE_PROMOTED);
crm_trace("%s%s now prefers %s%s",
rsc->id, (promoted_role_only? " (promoted)" : ""),
- pe__node_name(dest), force?"(forced)":"");
+ pcmk__node_name(dest), force?"(forced)":"");
/* only ban the previous location if current location != destination location.
* it is possible to use -M to enforce a location without regard of where the
@@ -2225,12 +2378,12 @@ cli_resource_move(const pcmk_resource_t *rsc, const char *rsc_id,
if(current) {
(void)cli_resource_ban(out, rsc_id, current->details->uname, move_lifetime,
cib, cib_options, promoted_role_only,
- PCMK__ROLE_PROMOTED);
+ PCMK_ROLE_PROMOTED);
} else if(count > 1) {
out->info(out, "Resource '%s' is currently %s in %d locations. "
"One may now move to %s",
rsc_id, (promoted_role_only? "promoted" : "active"),
- count, pe__node_name(dest));
+ count, pcmk__node_name(dest));
out->info(out, "To prevent '%s' from being %s at a specific location, "
"specify a node.",
rsc_id, (promoted_role_only? "promoted" : "active"));
diff --git a/tools/crm_rule.c b/tools/crm_rule.c
index 5cdc118..aa13de2 100644
--- a/tools/crm_rule.c
+++ b/tools/crm_rule.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2019-2023 the Pacemaker project contributors
+ * Copyright 2019-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -12,7 +12,7 @@
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/iso8601.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/pengine/rules_internal.h>
#include <crm/pengine/status.h>
#include <pacemaker-internal.h>
@@ -174,21 +174,22 @@ main(int argc, char **argv)
}
// Parse the input XML specified by the command-line options, if any
- if (pcmk__str_eq(options.input_xml, "-", pcmk__str_casei)) {
- input = stdin2xml();
+ if (pcmk__str_eq(options.input_xml, "-", pcmk__str_none)) {
+ input = pcmk__xml_read(NULL);
if (input == NULL) {
exit_code = CRM_EX_DATAERR;
- g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Couldn't parse input from STDIN\n");
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Couldn't parse input from STDIN");
goto done;
}
} else if (options.input_xml != NULL) {
- input = string2xml(options.input_xml);
+ input = pcmk__xml_parse(options.input_xml);
if (input == NULL) {
exit_code = CRM_EX_DATAERR;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
- "Couldn't parse input string: %s\n", options.input_xml);
+ "Couldn't parse input string: %s", options.input_xml);
goto done;
}
}
diff --git a/tools/crm_shadow.c b/tools/crm_shadow.c
index b86f462..aedf805 100644
--- a/tools/crm_shadow.c
+++ b/tools/crm_shadow.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -20,7 +20,6 @@
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
-#include <crm/msg_xml.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/ipc.h>
@@ -86,12 +85,12 @@ static struct {
* \internal
* \brief Display an instruction to the user
*
- * \param[in,out] out Output object
- * \param[in] ... Message arguments
+ * \param[in,out] out Output object
+ * \param[in] args Message-specific arguments
*
* \return Standard Pacemaker return code
*
- * \note The variadic message arguments are of the following format:
+ * \note \p args should contain the following:
* -# Instructional message
*/
PCMK__OUTPUT_ARGS("instruction", "const char *")
@@ -110,12 +109,12 @@ instruction_default(pcmk__output_t *out, va_list args)
* \internal
* \brief Display an instruction to the user
*
- * \param[in,out] out Output object
- * \param[in] ... Message arguments
+ * \param[in,out] out Output object
+ * \param[in] args Message-specific arguments
*
* \return Standard Pacemaker return code
*
- * \note The variadic message arguments are of the following format:
+ * \note \p args should contain the following:
* -# Instructional message
*/
PCMK__OUTPUT_ARGS("instruction", "const char *")
@@ -135,12 +134,12 @@ instruction_xml(pcmk__output_t *out, va_list args)
* \internal
* \brief Display information about a shadow CIB instance
*
- * \param[in,out] out Output object
- * \param[in] ... Message arguments
+ * \param[in,out] out Output object
+ * \param[in] args Message-specific arguments
*
* \return Standard Pacemaker return code
*
- * \note The variadic message arguments are of the following format:
+ * \note \p args should contain the following:
* -# Instance name (can be \p NULL)
* -# Shadow file name (can be \p NULL)
* -# Shadow file content (can be \p NULL)
@@ -170,12 +169,18 @@ shadow_default(pcmk__output_t *out, va_list args)
rc = out->info(out, "Content:");
if (content != NULL) {
- char *buf = pcmk__trim(dump_xml_formatted_with_text(content));
+ GString *buf = g_string_sized_new(1024);
+ gchar *str = NULL;
- if (!pcmk__str_empty(buf)) {
- out->info(out, "%s", buf);
+ pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text,
+ buf, 0);
+
+ str = g_string_free(buf, FALSE);
+ str = pcmk__trim(str);
+ if (!pcmk__str_empty(str)) {
+ out->info(out, "%s", str);
}
- free(buf);
+ g_free(str);
} else {
out->info(out, "<unknown>");
@@ -198,12 +203,12 @@ shadow_default(pcmk__output_t *out, va_list args)
* \internal
* \brief Display information about a shadow CIB instance
*
- * \param[in,out] out Output object
- * \param[in] ... Message arguments
+ * \param[in,out] out Output object
+ * \param[in] args Message-specific arguments
*
* \return Standard Pacemaker return code
*
- * \note The variadic message arguments are of the following format:
+ * \note \p args should contain the following:
* -# Instance name (can be \p NULL)
* -# Shadow file name (can be \p NULL)
* -# Shadow file content (can be \p NULL)
@@ -240,10 +245,16 @@ shadow_text(pcmk__output_t *out, va_list args)
rc = out->info(out, "%s", filename);
}
if (pcmk_is_set(flags, shadow_disp_content) && (content != NULL)) {
- char *buf = pcmk__trim(dump_xml_formatted_with_text(content));
+ GString *buf = g_string_sized_new(1024);
+ gchar *str = NULL;
+
+ pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text,
+ buf, 0);
- rc = out->info(out, "%s", pcmk__trim(buf));
- free(buf);
+ str = g_string_free(buf, FALSE);
+ str = pcmk__trim(str);
+ rc = out->info(out, "%s", str);
+ g_free(str);
}
if (pcmk_is_set(flags, shadow_disp_diff) && (diff != NULL)) {
rc = out->message(out, "xml-patchset", diff);
@@ -258,12 +269,12 @@ shadow_text(pcmk__output_t *out, va_list args)
* \internal
* \brief Display information about a shadow CIB instance
*
- * \param[in,out] out Output object
- * \param[in] ... Message arguments
+ * \param[in,out] out Output object
+ * \param[in] args Message-specific arguments
*
* \return Standard Pacemaker return code
*
- * \note The variadic message arguments are of the following format:
+ * \note \p args should contain the following:
* -# Instance name (can be \p NULL)
* -# Shadow file name (can be \p NULL)
* -# Shadow file content (can be \p NULL)
@@ -283,16 +294,19 @@ shadow_xml(pcmk__output_t *out, va_list args)
enum shadow_disp_flags flags G_GNUC_UNUSED =
(enum shadow_disp_flags) va_arg(args, int);
- pcmk__output_xml_create_parent(out, "shadow",
- "instance", instance,
- "file", filename,
+ pcmk__output_xml_create_parent(out, PCMK_XE_SHADOW,
+ PCMK_XA_INSTANCE, instance,
+ PCMK_XA_FILE, filename,
NULL);
if (content != NULL) {
- char *buf = dump_xml_formatted_with_text(content);
+ GString *buf = g_string_sized_new(1024);
+
+ pcmk__xml_string(content, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buf,
+ 0);
- out->output_xml(out, "content", buf);
- free(buf);
+ out->output_xml(out, PCMK_XE_CONTENT, buf->str);
+ g_string_free(buf, TRUE);
}
if (diff != NULL) {
@@ -498,7 +512,7 @@ read_xml(const char *filename, xmlNode **output, GError **error)
{
int rc = pcmk_rc_ok;
- *output = filename2xml(filename);
+ *output = pcmk__xml_read(filename);
if (*output == NULL) {
rc = pcmk_rc_no_input;
exit_code = pcmk_rc2exitc(rc);
@@ -521,18 +535,16 @@ static int
write_shadow_file(const xmlNode *xml, const char *filename, bool reset,
GError **error)
{
- int rc = write_xml_file(xml, filename, FALSE);
+ int rc = pcmk__xml_write_file(xml, filename, false, NULL);
- if (rc < 0) {
- rc = pcmk_legacy2rc(rc);
+ if (rc != pcmk_rc_ok) {
exit_code = pcmk_rc2exitc(rc);
g_set_error(error, PCMK__EXITC_ERROR, exit_code,
"Could not %s the shadow instance '%s': %s",
reset? "reset" : "create", options.instance,
pcmk_rc_str(rc));
- return rc;
}
- return pcmk_rc_ok;
+ return rc;
}
/*!
@@ -577,7 +589,7 @@ shadow_setup(pcmk__output_t *out, bool do_switch, GError **error)
setenv("PS1", new_prompt, 1);
setenv("CIB_shadow", options.instance, 1);
- out->message(out, "instruction",
+ out->message(out, PCMK_XE_INSTRUCTION,
"Press Ctrl+D to exit the crm_shadow shell");
if (pcmk__str_eq(shell, "(^|/)bash$", pcmk__str_regex)) {
@@ -683,8 +695,8 @@ commit_shadow_file(GError **error)
section_xml = input;
if (!options.full_upload) {
- section = XML_CIB_TAG_CONFIGURATION;
- section_xml = first_named_child(input, section);
+ section = PCMK_XE_CONFIGURATION;
+ section_xml = pcmk__xe_first_child(input, section, NULL, NULL);
}
rc = real_cib->cmds->replace(real_cib, section, section_xml,
@@ -726,9 +738,9 @@ create_shadow_empty(pcmk__output_t *out, GError **error)
}
output = createEmptyCib(0);
- crm_xml_add(output, XML_ATTR_VALIDATION, options.validate_with);
+ crm_xml_add(output, PCMK_XA_VALIDATE_WITH, options.validate_with);
out->info(out, "Created new %s configuration",
- crm_element_value(output, XML_ATTR_VALIDATION));
+ crm_element_value(output, PCMK_XA_VALIDATE_WITH));
if (write_shadow_file(output, filename, false, error) != pcmk_rc_ok) {
goto done;
diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c
index aab4110..81ff8b3 100644
--- a/tools/crm_simulate.c
+++ b/tools/crm_simulate.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2023 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -257,7 +257,7 @@ static GOptionEntry operation_entries[] = {
"N" },
/* Deprecated */
{ "pending", 'j', G_OPTION_FLAG_NO_ARG|G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, pending_cb,
- "Display pending state if 'record-pending' is enabled",
+ "Display pending state if '" PCMK_META_RECORD_PENDING "' is enabled",
NULL },
{ NULL }
@@ -359,22 +359,30 @@ setup_input(pcmk__output_t *out, const char *input, const char *output,
}
} else if (pcmk__str_eq(input, "-", pcmk__str_casei)) {
- cib_object = filename2xml(NULL);
+ cib_object = pcmk__xml_read(NULL);
} else {
- cib_object = filename2xml(input);
+ cib_object = pcmk__xml_read(input);
}
- if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) {
- create_xml_node(cib_object, XML_CIB_TAG_STATUS);
+ if (cib_object == NULL) {
+ rc = pcmk_rc_bad_input;
+ g_set_error(error, PCMK__EXITC_ERROR, pcmk_rc2exitc(rc),
+ "Could not read input XML: %s", pcmk_rc_str(rc));
+ return rc;
+ }
+
+ if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) {
+ pcmk__xe_create(cib_object, PCMK_XE_STATUS);
}
- if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
+ rc = pcmk_update_configured_schema(&cib_object, false);
+ if (rc != pcmk_rc_ok) {
free_xml(cib_object);
- return pcmk_rc_transform_failed;
+ return rc;
}
- if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
+ if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) {
free_xml(cib_object);
return pcmk_rc_schema_validation;
}
@@ -388,20 +396,17 @@ setup_input(pcmk__output_t *out, const char *input, const char *output,
free(pid);
}
- rc = write_xml_file(cib_object, output, FALSE);
- free_xml(cib_object);
- cib_object = NULL;
-
- if (rc < 0) {
- rc = pcmk_legacy2rc(rc);
+ rc = pcmk__xml_write_file(cib_object, output, false, NULL);
+ if (rc != pcmk_rc_ok) {
g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT,
"Could not create '%s': %s", output, pcmk_rc_str(rc));
- return rc;
} else {
setenv("CIB_file", output, 1);
- free(local_output);
- return pcmk_rc_ok;
}
+
+ free_xml(cib_object);
+ free(local_output);
+ return rc;
}
static GOptionContext *
@@ -489,9 +494,8 @@ main(int argc, char **argv)
if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches) &&
!pcmk_is_set(options.flags, pcmk_sim_show_scores) &&
!pcmk_is_set(options.flags, pcmk_sim_show_utilization)) {
- pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname());
- } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
- pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname());
+
+ pcmk__output_text_set_fancy(out, true);
}
pe__register_messages(out);
@@ -523,12 +527,12 @@ main(int argc, char **argv)
}
if (pcmk_is_set(options.flags, pcmk_sim_show_scores)) {
- pe__set_working_set_flags(scheduler, pcmk_sched_output_scores);
+ pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores);
}
if (pcmk_is_set(options.flags, pcmk_sim_show_utilization)) {
- pe__set_working_set_flags(scheduler, pcmk_sched_show_utilization);
+ pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization);
}
- pe__set_working_set_flags(scheduler, pcmk_sched_no_compat);
+ pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_compat);
if (options.test_dir != NULL) {
scheduler->priv = out;
diff --git a/tools/crm_ticket.c b/tools/crm_ticket.c
index d95b581..2faa74e 100644
--- a/tools/crm_ticket.c
+++ b/tools/crm_ticket.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2023 the Pacemaker project contributors
+ * Copyright 2012-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -22,7 +22,6 @@
#include <fcntl.h>
#include <libgen.h>
-#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/ipc.h>
#include <crm/common/cmdline_internal.h>
@@ -60,9 +59,17 @@ GList *attr_delete;
GHashTable *attr_set;
bool modified = false;
int cib_options = cib_sync_call;
+static pcmk__output_t *out = NULL;
#define INDENT " "
+static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
+ PCMK__SUPPORTED_FORMAT_XML,
+ { NULL, NULL, NULL }
+};
+
static gboolean
attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
pcmk__str_update(&options.attr_value, optarg);
@@ -71,7 +78,7 @@ attr_value_cb(const gchar *option_name, const gchar *optarg, gpointer data, GErr
return TRUE;
}
- g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value));
+ pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
pcmk__str_update(&options.attr_name, NULL);
pcmk__str_update(&options.attr_value, NULL);
@@ -116,16 +123,16 @@ get_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError
static gboolean
grant_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
if (pcmk__str_any_of(option_name, "--grant", "-g", NULL)) {
- g_hash_table_insert(attr_set, strdup("granted"), strdup("true"));
+ pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_TRUE);
modified = true;
} else if (pcmk__str_any_of(option_name, "--revoke", "-r", NULL)) {
- g_hash_table_insert(attr_set, strdup("granted"), strdup("false"));
+ pcmk__insert_dup(attr_set, PCMK__XA_GRANTED, PCMK_VALUE_FALSE);
modified = true;
} else if (pcmk__str_any_of(option_name, "--standby", "-s", NULL)) {
- g_hash_table_insert(attr_set, strdup("standby"), strdup("true"));
+ pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_TRUE);
modified = true;
} else if (pcmk__str_any_of(option_name, "--activate", "-a", NULL)) {
- g_hash_table_insert(attr_set, strdup("standby"), strdup("false"));
+ pcmk__insert_dup(attr_set, PCMK_XA_STANDBY, PCMK_VALUE_FALSE);
modified = true;
}
@@ -140,7 +147,7 @@ set_attr_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError
return TRUE;
}
- g_hash_table_insert(attr_set, strdup(options.attr_name), strdup(options.attr_value));
+ pcmk__insert_dup(attr_set, options.attr_name, options.attr_value);
pcmk__str_update(&options.attr_name, NULL);
pcmk__str_update(&options.attr_value, NULL);
@@ -167,7 +174,7 @@ static GOptionEntry query_entries[] = {
NULL },
{ "constraints", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, command_cb,
- "Display the rsc_ticket constraints that apply to ticket(s)",
+ "Display the " PCMK_XE_RSC_TICKET " constraints that apply to ticket(s)",
NULL },
{ NULL }
@@ -240,7 +247,7 @@ static GOptionEntry addl_entries[] = {
static GOptionEntry deprecated_entries[] = {
{ "set-name", 'n', 0, G_OPTION_ARG_STRING, &options.set_name,
- "(Advanced) ID of the instance_attributes object to change",
+ "(Advanced) ID of the " PCMK_XE_INSTANCE_ATTRIBUTES " object to change",
"ID" },
{ "nvpair", 'i', 0, G_OPTION_ARG_STRING, &options.attr_id,
@@ -254,420 +261,37 @@ static GOptionEntry deprecated_entries[] = {
{ NULL }
};
-static pcmk_ticket_t *
-find_ticket(gchar *ticket_id, pcmk_scheduler_t *scheduler)
-{
- return g_hash_table_lookup(scheduler->tickets, ticket_id);
-}
-
static void
-print_date(time_t time)
+ticket_grant_warning(gchar *ticket_id)
{
- int lpc = 0;
- char date_str[26];
-
- asctime_r(localtime(&time), date_str);
- for (; lpc < 26; lpc++) {
- if (date_str[lpc] == '\n') {
- date_str[lpc] = 0;
- }
- }
- fprintf(stdout, "'%s'", date_str);
+ out->err(out, "This command cannot help you verify whether '%s' has "
+ "been already granted elsewhere.\n"
+ "If you really want to grant '%s' to this site now, and "
+ "you know what you are doing,\n"
+ "please specify --force.",
+ ticket_id, ticket_id);
}
static void
-print_ticket(pcmk_ticket_t *ticket, bool raw, bool details)
-{
- if (raw) {
- fprintf(stdout, "%s\n", ticket->id);
- return;
- }
-
- fprintf(stdout, "%s\t%s %s",
- ticket->id, ticket->granted ? "granted" : "revoked",
- ticket->standby ? "[standby]" : " ");
-
- if (details && g_hash_table_size(ticket->state) > 0) {
- GHashTableIter iter;
- const char *name = NULL;
- const char *value = NULL;
- int lpc = 0;
-
- fprintf(stdout, " (");
-
- g_hash_table_iter_init(&iter, ticket->state);
- while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
- if (lpc > 0) {
- fprintf(stdout, ", ");
- }
- fprintf(stdout, "%s=", name);
- if (pcmk__str_any_of(name, "last-granted", "expires", NULL)) {
- long long time_ll;
-
- pcmk__scan_ll(value, &time_ll, 0);
- print_date((time_t) time_ll);
- } else {
- fprintf(stdout, "%s", value);
- }
- lpc++;
- }
-
- fprintf(stdout, ")\n");
-
- } else {
- if (ticket->last_granted > -1) {
- fprintf(stdout, " last-granted=");
- print_date(ticket->last_granted);
- }
- fprintf(stdout, "\n");
- }
-
- return;
-}
-
-static void
-print_ticket_list(pcmk_scheduler_t *scheduler, bool raw, bool details)
-{
- GHashTableIter iter;
- pcmk_ticket_t *ticket = NULL;
-
- g_hash_table_iter_init(&iter, scheduler->tickets);
-
- while (g_hash_table_iter_next(&iter, NULL, (void **)&ticket)) {
- print_ticket(ticket, raw, details);
- }
-}
-
-static int
-find_ticket_state(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_state_xml)
-{
- int rc = pcmk_rc_ok;
- xmlNode *xml_search = NULL;
-
- GString *xpath = NULL;
-
- CRM_ASSERT(ticket_state_xml != NULL);
- *ticket_state_xml = NULL;
-
- xpath = g_string_sized_new(1024);
- g_string_append(xpath,
- "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS
- "/" XML_CIB_TAG_TICKETS);
-
- if (ticket_id != NULL) {
- pcmk__g_strcat(xpath,
- "/" XML_CIB_TAG_TICKET_STATE
- "[@" XML_ATTR_ID "=\"", ticket_id, "\"]", NULL);
- }
-
- rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
- cib_sync_call | cib_scope_local | cib_xpath);
- rc = pcmk_legacy2rc(rc);
- g_string_free(xpath, TRUE);
-
- if (rc != pcmk_rc_ok) {
- return rc;
- }
-
- crm_log_xml_debug(xml_search, "Match");
- if (xml_search->children != NULL) {
- if (ticket_id) {
- fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id);
- }
- *ticket_state_xml = xml_search;
- } else {
- *ticket_state_xml = xml_search;
- }
- return rc;
-}
-
-static int
-find_ticket_constraints(cib_t * the_cib, gchar *ticket_id, xmlNode ** ticket_cons_xml)
-{
- int rc = pcmk_rc_ok;
- xmlNode *xml_search = NULL;
-
- GString *xpath = NULL;
- const char *xpath_base = NULL;
-
- CRM_ASSERT(ticket_cons_xml != NULL);
- *ticket_cons_xml = NULL;
-
- xpath_base = pcmk_cib_xpath_for(XML_CIB_TAG_CONSTRAINTS);
- if (xpath_base == NULL) {
- crm_err(XML_CIB_TAG_CONSTRAINTS " CIB element not known (bug?)");
- return -ENOMSG;
- }
-
- xpath = g_string_sized_new(1024);
- pcmk__g_strcat(xpath, xpath_base, "/" XML_CONS_TAG_RSC_TICKET, NULL);
-
- if (ticket_id != NULL) {
- pcmk__g_strcat(xpath,
- "[@" XML_TICKET_ATTR_TICKET "=\"", ticket_id, "\"]",
- NULL);
- }
-
- rc = the_cib->cmds->query(the_cib, (const char *) xpath->str, &xml_search,
- cib_sync_call | cib_scope_local | cib_xpath);
- rc = pcmk_legacy2rc(rc);
- g_string_free(xpath, TRUE);
-
- if (rc != pcmk_rc_ok) {
- return rc;
- }
-
- crm_log_xml_debug(xml_search, "Match");
- *ticket_cons_xml = xml_search;
-
- return rc;
-}
-
-static int
-dump_ticket_xml(cib_t * the_cib, gchar *ticket_id)
-{
- int rc = pcmk_rc_ok;
- xmlNode *state_xml = NULL;
-
- rc = find_ticket_state(the_cib, ticket_id, &state_xml);
-
- if (state_xml == NULL) {
- return rc;
- }
-
- fprintf(stdout, "State XML:\n");
- if (state_xml) {
- char *state_xml_str = NULL;
-
- state_xml_str = dump_xml_formatted(state_xml);
- fprintf(stdout, "\n%s", state_xml_str);
- free_xml(state_xml);
- free(state_xml_str);
- }
-
- return rc;
-}
-
-static int
-dump_constraints(cib_t * the_cib, gchar *ticket_id)
-{
- int rc = pcmk_rc_ok;
- xmlNode *cons_xml = NULL;
- char *cons_xml_str = NULL;
-
- rc = find_ticket_constraints(the_cib, ticket_id, &cons_xml);
-
- if (cons_xml == NULL) {
- return rc;
- }
-
- cons_xml_str = dump_xml_formatted(cons_xml);
- fprintf(stdout, "Constraints XML:\n\n%s", cons_xml_str);
- free_xml(cons_xml);
- free(cons_xml_str);
-
- return rc;
-}
-
-static int
-get_ticket_state_attr(gchar *ticket_id, const char *attr_name, const char **attr_value,
- pcmk_scheduler_t *scheduler)
-{
- pcmk_ticket_t *ticket = NULL;
-
- CRM_ASSERT(attr_value != NULL);
- *attr_value = NULL;
-
- ticket = g_hash_table_lookup(scheduler->tickets, ticket_id);
- if (ticket == NULL) {
- return ENXIO;
- }
-
- *attr_value = g_hash_table_lookup(ticket->state, attr_name);
- if (*attr_value == NULL) {
- return ENXIO;
- }
-
- return pcmk_rc_ok;
-}
-
-static void
-ticket_warning(gchar *ticket_id, const char *action)
-{
- GString *warning = g_string_sized_new(1024);
- const char *word = NULL;
-
- CRM_ASSERT(action != NULL);
-
- if (strcmp(action, "grant") == 0) {
- pcmk__g_strcat(warning,
- "This command cannot help you verify whether '",
- ticket_id,
- "' has been already granted elsewhere.\n", NULL);
- word = "to";
-
- } else {
- pcmk__g_strcat(warning,
- "Revoking '", ticket_id, "' can trigger the specified "
- "'loss-policy'(s) relating to '", ticket_id, "'.\n\n"
- "You can check that with:\n"
- "crm_ticket --ticket ", ticket_id, " --constraints\n\n"
- "Otherwise before revoking '", ticket_id, "', "
- "you may want to make '", ticket_id, "' "
- "standby with:\n"
- "crm_ticket --ticket ", ticket_id, " --standby\n\n",
- NULL);
- word = "from";
- }
-
- pcmk__g_strcat(warning,
- "If you really want to ", action, " '", ticket_id, "' ",
- word, " this site now, and you know what you are doing,\n"
- "please specify --force.", NULL);
-
- fprintf(stdout, "%s\n", (const char *) warning->str);
-
- g_string_free(warning, TRUE);
-}
-
-static bool
-allow_modification(gchar *ticket_id)
+ticket_revoke_warning(gchar *ticket_id)
{
- const char *value = NULL;
- GList *list_iter = NULL;
-
- if (options.force) {
- return true;
- }
-
- if (g_hash_table_lookup_extended(attr_set, "granted", NULL, (gpointer *) & value)) {
- if (crm_is_true(value)) {
- ticket_warning(ticket_id, "grant");
- return false;
-
- } else {
- ticket_warning(ticket_id, "revoke");
- return false;
- }
- }
-
- for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
- const char *key = (const char *)list_iter->data;
-
- if (pcmk__str_eq(key, "granted", pcmk__str_casei)) {
- ticket_warning(ticket_id, "revoke");
- return false;
- }
- }
-
- return true;
-}
-
-static int
-modify_ticket_state(gchar *ticket_id, cib_t *cib, pcmk_scheduler_t *scheduler)
-{
- int rc = pcmk_rc_ok;
- xmlNode *xml_top = NULL;
- xmlNode *ticket_state_xml = NULL;
- bool found = false;
-
- GList *list_iter = NULL;
- GHashTableIter hash_iter;
-
- char *key = NULL;
- char *value = NULL;
-
- pcmk_ticket_t *ticket = NULL;
-
- rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
- if (rc == pcmk_rc_ok) {
- crm_debug("Found a match state for ticket: id=%s", ticket_id);
- xml_top = ticket_state_xml;
- found = true;
-
- } else if (rc != ENXIO) {
- return rc;
-
- } else if (g_hash_table_size(attr_set) == 0){
- return pcmk_rc_ok;
-
- } else {
- xmlNode *xml_obj = NULL;
-
- xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
- xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
- ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE);
- crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id);
- }
-
- for(list_iter = attr_delete; list_iter; list_iter = list_iter->next) {
- const char *key = (const char *)list_iter->data;
- xml_remove_prop(ticket_state_xml, key);
- }
-
- ticket = find_ticket(ticket_id, scheduler);
-
- g_hash_table_iter_init(&hash_iter, attr_set);
- while (g_hash_table_iter_next(&hash_iter, (gpointer *) & key, (gpointer *) & value)) {
- crm_xml_add(ticket_state_xml, key, value);
-
- if (pcmk__str_eq(key, "granted", pcmk__str_casei)
- && (ticket == NULL || ticket->granted == FALSE)
- && crm_is_true(value)) {
-
- char *now = pcmk__ttoa(time(NULL));
-
- crm_xml_add(ticket_state_xml, "last-granted", now);
- free(now);
- }
- }
-
- if (found && (attr_delete != NULL)) {
- crm_log_xml_debug(xml_top, "Replace");
- rc = cib->cmds->replace(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
- rc = pcmk_legacy2rc(rc);
-
- } else {
- crm_log_xml_debug(xml_top, "Update");
- rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options);
- rc = pcmk_legacy2rc(rc);
- }
-
- free_xml(xml_top);
- return rc;
-}
-
-static int
-delete_ticket_state(gchar *ticket_id, cib_t * cib)
-{
- xmlNode *ticket_state_xml = NULL;
-
- int rc = pcmk_rc_ok;
-
- rc = find_ticket_state(cib, ticket_id, &ticket_state_xml);
-
- if (rc == ENXIO) {
- return pcmk_rc_ok;
-
- } else if (rc != pcmk_rc_ok) {
- return rc;
- }
-
- crm_log_xml_debug(ticket_state_xml, "Delete");
-
- rc = cib->cmds->remove(cib, XML_CIB_TAG_STATUS, ticket_state_xml, cib_options);
- rc = pcmk_legacy2rc(rc);
-
- if (rc == pcmk_rc_ok) {
- fprintf(stdout, "Cleaned up %s\n", ticket_id);
- }
-
- free_xml(ticket_state_xml);
- return rc;
+ out->err(out, "Revoking '%s' can trigger the specified '" PCMK_XA_LOSS_POLICY
+ "'(s) relating to '%s'.\n\n"
+ "You can check that with:\n"
+ "crm_ticket --ticket %s --constraints\n\n"
+ "Otherwise before revoking '%s', you may want to make '%s'"
+ "standby with:\n"
+ "crm_ticket --ticket %s --standby\n\n"
+ "If you really want to revoke '%s' from this site now, and "
+ "you know what you are doing,\n"
+ "please specify --force.",
+ ticket_id, ticket_id, ticket_id, ticket_id, ticket_id,
+ ticket_id, ticket_id);
}
static GOptionContext *
-build_arg_context(pcmk__common_args_t *args) {
+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
+{
GOptionContext *context = NULL;
const char *description = "Examples:\n\n"
@@ -677,7 +301,7 @@ build_arg_context(pcmk__common_args_t *args) {
"\tcrm_ticket --details\n\n"
"Display the XML of 'ticketA':\n\n"
"\tcrm_ticket --ticket ticketA --query-xml\n\n"
- "Display the rsc_ticket constraints that apply to 'ticketA':\n\n"
+ "Display the " PCMK_XE_RSC_TICKET " constraints that apply to 'ticketA':\n\n"
"\tcrm_ticket --ticket ticketA --constraints\n\n"
"Grant 'ticketA' to this cluster site:\n\n"
"\tcrm_ticket --ticket ticketA --grant\n\n"
@@ -699,7 +323,7 @@ build_arg_context(pcmk__common_args_t *args) {
"causing the cluster site to 'forget' the existing ticket state:\n\n"
"\tcrm_ticket --ticket ticketA --cleanup\n\n";
- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
g_option_context_set_description(context, description);
pcmk__add_arg_group(context, "queries", "Queries:",
@@ -726,6 +350,7 @@ main(int argc, char **argv)
crm_exit_t exit_code = CRM_EX_OK;
int rc = pcmk_rc_ok;
+ GOptionGroup *output_group = NULL;
pcmk__common_args_t *args = NULL;
GOptionContext *context = NULL;
gchar **processed_args = NULL;
@@ -734,9 +359,10 @@ main(int argc, char **argv)
attr_delete = NULL;
args = pcmk__new_common_args(SUMMARY);
- context = build_arg_context(args);
+ context = build_arg_context(args, &output_group);
processed_args = pcmk__cmdline_preproc(argv, "dintvxCDGS");
+ pcmk__register_formats(output_group, formats);
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
exit_code = CRM_EX_USAGE;
goto done;
@@ -744,11 +370,21 @@ main(int argc, char **argv)
pcmk__cli_init_logging("crm_ticket", args->verbosity);
+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
+ if (rc != pcmk_rc_ok) {
+ exit_code = pcmk_rc2exitc(rc);
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Error creating output format %s: %s", args->output_ty,
+ pcmk_rc_str(rc));
+ goto done;
+ }
+
+ pe__register_messages(out);
+ pcmk__register_lib_messages(out);
+
if (args->version) {
- g_strfreev(processed_args);
- pcmk__free_arg_context(context);
- /* FIXME: When crm_ticket is converted to use formatted output, this can go. */
- pcmk__cli_help('v');
+ out->version(out, false);
+ goto done;
}
scheduler = pe_new_working_set();
@@ -759,7 +395,7 @@ main(int argc, char **argv)
"Could not allocate scheduler data: %s", pcmk_rc_str(rc));
goto done;
}
- pe__set_working_set_flags(scheduler,
+ pcmk__set_scheduler_flags(scheduler,
pcmk_sched_no_counts|pcmk_sched_no_compat);
cib_conn = cib_new();
@@ -780,7 +416,7 @@ main(int argc, char **argv)
}
if (options.xml_file != NULL) {
- cib_xml_copy = filename2xml(options.xml_file);
+ cib_xml_copy = pcmk__xml_read(options.xml_file);
} else {
rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call);
@@ -794,8 +430,9 @@ main(int argc, char **argv)
}
}
- if (!cli_config_update(&cib_xml_copy, NULL, FALSE)) {
- exit_code = CRM_EX_CONFIG;
+ rc = pcmk_update_configured_schema(&cib_xml_copy, false);
+ if (rc != pcmk_rc_ok) {
+ exit_code = pcmk_rc2exitc(rc);
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Could not update local CIB to latest schema version");
goto done;
@@ -806,8 +443,9 @@ main(int argc, char **argv)
cluster_status(scheduler);
- /* For recording the tickets that are referenced in rsc_ticket constraints
- * but have never been granted yet. */
+ /* For recording the tickets that are referenced in PCMK_XE_RSC_TICKET
+ * constraints but have never been granted yet.
+ */
pcmk__unpack_constraints(scheduler);
if (options.ticket_cmd == 'l' || options.ticket_cmd == 'L' || options.ticket_cmd == 'w') {
@@ -820,32 +458,30 @@ main(int argc, char **argv)
raw = true;
}
- if (options.ticket_id) {
- pcmk_ticket_t *ticket = find_ticket(options.ticket_id, scheduler);
-
- if (ticket == NULL) {
- exit_code = CRM_EX_NOSUCH;
- g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
- "No such ticket '%s'", options.ticket_id);
- goto done;
- }
- print_ticket(ticket, raw, details);
+ rc = pcmk__ticket_info(out, scheduler, options.ticket_id, details, raw);
+ exit_code = pcmk_rc2exitc(rc);
- } else {
- print_ticket_list(scheduler, raw, details);
+ if (rc == ENXIO) {
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "No such ticket '%s'", options.ticket_id);
+ } else if (rc != pcmk_rc_ok) {
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Could not get ticket info: %s", pcmk_rc_str(rc));
}
} else if (options.ticket_cmd == 'q') {
- rc = dump_ticket_xml(cib_conn, options.ticket_id);
- exit_code = pcmk_rc2exitc(rc);
+ rc = pcmk__ticket_state(out, cib_conn, options.ticket_id);
- if (rc != pcmk_rc_ok) {
+ if (rc != pcmk_rc_ok && rc != pcmk_rc_duplicate_id) {
+ exit_code = pcmk_rc2exitc(rc);
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Could not query ticket XML: %s", pcmk_rc_str(rc));
+ } else {
+ exit_code = CRM_EX_OK;
}
} else if (options.ticket_cmd == 'c') {
- rc = dump_constraints(cib_conn, options.ticket_id);
+ rc = pcmk__ticket_constraints(out, cib_conn, options.ticket_id);
exit_code = pcmk_rc2exitc(rc);
if (rc != pcmk_rc_ok) {
@@ -854,8 +490,6 @@ main(int argc, char **argv)
}
} else if (options.ticket_cmd == 'G') {
- const char *value = NULL;
-
if (options.ticket_id == NULL) {
exit_code = CRM_EX_NOSUCH;
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
@@ -863,14 +497,8 @@ main(int argc, char **argv)
goto done;
}
- rc = get_ticket_state_attr(options.ticket_id, options.get_attr_name,
- &value, scheduler);
- if (rc == pcmk_rc_ok) {
- fprintf(stdout, "%s\n", value);
- } else if (rc == ENXIO && options.attr_default) {
- fprintf(stdout, "%s\n", options.attr_default);
- rc = pcmk_rc_ok;
- }
+ rc = pcmk__ticket_get_attr(out, scheduler, options.ticket_id,
+ options.get_attr_name, options.attr_default);
exit_code = pcmk_rc2exitc(rc);
} else if (options.ticket_cmd == 'C') {
@@ -881,30 +509,28 @@ main(int argc, char **argv)
goto done;
}
- if (options.force == FALSE) {
- pcmk_ticket_t *ticket = NULL;
+ rc = pcmk__ticket_delete(out, cib_conn, scheduler, options.ticket_id,
+ options.force);
+ exit_code = pcmk_rc2exitc(rc);
- ticket = find_ticket(options.ticket_id, scheduler);
- if (ticket == NULL) {
- exit_code = CRM_EX_NOSUCH;
+ switch (rc) {
+ case ENXIO:
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"No such ticket '%s'", options.ticket_id);
- goto done;
- }
+ break;
- if (ticket->granted) {
- ticket_warning(options.ticket_id, "revoke");
- exit_code = CRM_EX_INSUFFICIENT_PRIV;
- goto done;
- }
- }
+ case EACCES:
+ ticket_revoke_warning(options.ticket_id);
+ break;
- rc = delete_ticket_state(options.ticket_id, cib_conn);
- exit_code = pcmk_rc2exitc(rc);
+ case pcmk_rc_ok:
+ case pcmk_rc_duplicate_id:
+ break;
- if (rc != pcmk_rc_ok) {
- g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
- "Could not clean up ticket: %s", pcmk_rc_str(rc));
+ default:
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Could not clean up ticket: %s", pcmk_rc_str(rc));
+ break;
}
} else if (modified) {
@@ -931,17 +557,39 @@ main(int argc, char **argv)
goto done;
}
- if (!allow_modification(options.ticket_id)) {
- exit_code = CRM_EX_INSUFFICIENT_PRIV;
- g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
- "Ticket modification not allowed");
- goto done;
+ if (attr_delete != NULL) {
+ rc = pcmk__ticket_remove_attr(out, cib_conn, scheduler, options.ticket_id,
+ attr_delete, options.force);
+
+ if (rc == EACCES) {
+ ticket_revoke_warning(options.ticket_id);
+ exit_code = pcmk_rc2exitc(rc);
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Ticket modification not allowed without --force");
+ }
+ } else {
+ rc = pcmk__ticket_set_attr(out, cib_conn, scheduler, options.ticket_id,
+ attr_set, options.force);
+
+ if (rc == EACCES) {
+ const char *value = NULL;
+
+ value = g_hash_table_lookup(attr_set, PCMK__XA_GRANTED);
+ if (crm_is_true(value)) {
+ ticket_grant_warning(options.ticket_id);
+ } else {
+ ticket_revoke_warning(options.ticket_id);
+ }
+
+ exit_code = pcmk_rc2exitc(rc);
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Ticket modification not allowed without --force");
+ }
}
- rc = modify_ticket_state(options.ticket_id, cib_conn, scheduler);
exit_code = pcmk_rc2exitc(rc);
- if (rc != pcmk_rc_ok) {
+ if (rc != pcmk_rc_ok && error == NULL) {
g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
"Could not modify ticket: %s", pcmk_rc_str(rc));
}
@@ -1005,7 +653,13 @@ main(int argc, char **argv)
g_free(options.ticket_id);
g_free(options.xml_file);
- pcmk__output_and_clear_error(&error, NULL);
+ pcmk__output_and_clear_error(&error, out);
+
+ if (out != NULL) {
+ out->finish(out, exit_code, true, NULL);
+ pcmk__output_free(out);
+ }
+ pcmk__unregister_formats();
crm_exit(exit_code);
}
diff --git a/tools/crm_verify.c b/tools/crm_verify.c
index 199814e..81e375a 100644
--- a/tools/crm_verify.c
+++ b/tools/crm_verify.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -24,7 +24,6 @@
#include <crm/common/xml.h>
#include <crm/common/util.h>
-#include <crm/msg_xml.h>
#include <crm/cib.h>
#include <crm/cib/internal.h>
#include <crm/pengine/status.h>
@@ -43,6 +42,7 @@ struct {
char *xml_file;
gboolean xml_stdin;
char *xml_string;
+ unsigned int verbosity;
} options;
static GOptionEntry data_entries[] = {
@@ -112,11 +112,33 @@ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
return context;
}
+/*!
+ * \internal
+ * \brief Output a configuration issue
+ *
+ * \param[in] ctx Output object
+ * \param[in] msg printf(3)-style format string
+ * \param[in] ... Format string arguments
+ */
+G_GNUC_PRINTF(2, 3)
+static void
+output_config_issue(void *ctx, const char *msg, ...)
+{
+ va_list ap;
+ char *buf = NULL;
+ pcmk__output_t *out = ctx;
+
+ va_start(ap, msg);
+ CRM_ASSERT(vasprintf(&buf, msg, ap) > 0);
+ if (options.verbosity > 0) {
+ out->err(out, "%s", buf);
+ }
+ va_end(ap);
+}
+
int
main(int argc, char **argv)
{
- xmlNode *cib_object = NULL;
- xmlNode *status = NULL;
pcmk_scheduler_t *scheduler = NULL;
@@ -127,7 +149,13 @@ main(int argc, char **argv)
pcmk__output_t *out = NULL;
+ const char *cib_source = NULL;
+ xmlNode *cib_object = NULL;
+
GOptionGroup *output_group = NULL;
+
+ const char *failure_type = NULL;
+
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
GOptionContext *context = build_arg_context(args, &output_group);
@@ -159,127 +187,73 @@ main(int argc, char **argv)
pcmk__register_lib_messages(out);
- pcmk__set_config_error_handler((pcmk__config_error_func) out->err, out);
- pcmk__set_config_warning_handler((pcmk__config_warning_func) out->err, out);
+ pcmk__set_config_error_handler(output_config_issue, out);
+ pcmk__set_config_warning_handler(output_config_issue, out);
- crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
-
- if (options.use_live_cib) {
- crm_info("Reading XML from: live cluster");
- rc = cib__signon_query(out, NULL, &cib_object);
-
- if (rc != pcmk_rc_ok) {
- // cib__signon_query() outputs any relevant error
- goto done;
- }
-
- } else if (options.xml_file != NULL) {
- cib_object = filename2xml(options.xml_file);
- if (cib_object == NULL) {
- rc = ENODATA;
- g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file);
- goto done;
- }
+ if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
+ args->verbosity = 1;
+ }
+ options.verbosity = args->verbosity;
+ if (options.xml_file != NULL) {
+ cib_source = options.xml_file;
} else if (options.xml_string != NULL) {
- cib_object = string2xml(options.xml_string);
- if (cib_object == NULL) {
- rc = ENODATA;
- g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string);
- goto done;
- }
+ cib_source = options.xml_string;
} else if (options.xml_stdin) {
- cib_object = stdin2xml();
- if (cib_object == NULL) {
- rc = ENODATA;
- g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN.");
- goto done;
- }
-
+ cib_source = "-";
+ } else if (options.use_live_cib) {
+ cib_source = NULL;
} else {
rc = ENODATA;
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "No configuration source specified. Use --help for usage information.");
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No input specified");
goto done;
}
- if (!pcmk__xe_is(cib_object, XML_TAG_CIB)) {
- rc = EBADMSG;
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "This tool can only check complete configurations (i.e. those starting with <cib>).");
+ rc = pcmk__parse_cib(out, cib_source, &cib_object);
+
+ if (rc != pcmk_rc_ok) {
+ g_set_error(&error, PCMK__EXITC_ERROR, rc, "Couldn't parse input");
goto done;
}
if (options.cib_save != NULL) {
- write_xml_file(cib_object, options.cib_save, FALSE);
- }
-
- status = pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS);
- if (status == NULL) {
- create_xml_node(cib_object, XML_CIB_TAG_STATUS);
- }
-
- if (pcmk__validate_xml(cib_object, NULL, (xmlRelaxNGValidityErrorFunc) out->err, out) == FALSE) {
- pcmk__config_err("CIB did not pass schema validation");
- free_xml(cib_object);
- cib_object = NULL;
-
- } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
- crm_config_error = TRUE;
- free_xml(cib_object);
- cib_object = NULL;
- out->err(out, "The cluster will NOT be able to use this configuration.\n"
- "Please manually update the configuration to conform to the %s syntax.",
- xml_latest_schema());
+ pcmk__xml_write_file(cib_object, options.cib_save, false, NULL);
}
scheduler = pe_new_working_set();
+
if (scheduler == NULL) {
rc = errno;
g_set_error(&error, PCMK__RC_ERROR, rc,
"Could not allocate scheduler data: %s", pcmk_rc_str(rc));
goto done;
}
+
scheduler->priv = out;
- /* Process the configuration to set crm_config_error/crm_config_warning.
- *
- * @TODO Some parts of the configuration are unpacked only when needed (for
- * example, action configuration), so we aren't necessarily checking those.
- */
- if (cib_object != NULL) {
- unsigned long long flags = pcmk_sched_no_counts|pcmk_sched_no_compat;
+ rc = pcmk__verify(scheduler, out, cib_object);
- if ((status == NULL) && !options.use_live_cib) {
- // No status available, so do minimal checks
- flags |= pcmk_sched_validate_only;
- }
- pcmk__schedule_actions(cib_object, flags, scheduler);
- }
- pe_free_working_set(scheduler);
+ if (rc == pcmk_rc_schema_validation) {
+ if (crm_config_error) {
+ failure_type = "Errors found during check: ";
+ } else if (crm_config_warning) {
+ failure_type = "Warnings found during check: ";
+ } else {
+ failure_type = "";
+ }
+
+ if (args->quiet) {
+ // User requested no output
- if (crm_config_error) {
- rc = pcmk_rc_schema_validation;
-
- if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "Errors found during check: config not valid");
- } else {
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "Errors found during check: config not valid\n-V may provide more details");
- }
-
- } else if (crm_config_warning) {
- rc = pcmk_rc_schema_validation;
-
- if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "Warnings found during check: config may not be valid");
- } else {
- g_set_error(&error, PCMK__RC_ERROR, rc,
- "Warnings found during check: config may not be valid\n-V may provide more details");
+ } else if (options.verbosity > 0) {
+ out->err(out, "%sconfig not valid", failure_type);
+
+ } else {
+ out->err(out, "%sconfig not valid\n-V may provide more details", failure_type);
+ }
}
- }
+
+ pe_free_working_set(scheduler);
done:
g_strfreev(processed_args);
@@ -288,6 +262,10 @@ main(int argc, char **argv)
free(options.xml_file);
free(options.xml_string);
+ if (cib_object != NULL) {
+ free_xml(cib_object);
+ }
+
if (exit_code == CRM_EX_OK) {
exit_code = pcmk_rc2exitc(rc);
}
diff --git a/tools/crmadmin.c b/tools/crmadmin.c
index 0b400ae..082904f 100644
--- a/tools/crmadmin.c
+++ b/tools/crmadmin.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2023 the Pacemaker project contributors
+ * Copyright 2004-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -33,7 +33,7 @@ static enum {
struct {
gboolean health;
- gint timeout;
+ guint timeout;
char *optarg;
char *ipc_name;
gboolean bash_export;
@@ -117,11 +117,7 @@ command_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError
}
if (!strcmp(option_name, "--timeout") || !strcmp(option_name, "-t")) {
- options.timeout = crm_parse_interval_spec(optarg);
- if (errno == EINVAL) {
- return FALSE;
- }
- return TRUE;
+ return pcmk_parse_interval_spec(optarg, &options.timeout) == pcmk_rc_ok;
}
pcmk__str_update(&options.optarg, optarg);
@@ -205,10 +201,6 @@ main(int argc, char **argv)
out->quiet = args->quiet;
- if (!pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname())) {
- goto done;
- }
-
if (args->version) {
out->version(out, false);
goto done;
diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c
index 01f72d5..2268e0d 100644
--- a/tools/stonith_admin.c
+++ b/tools/stonith_admin.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009-2023 the Pacemaker project contributors
+ * Copyright 2009-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -23,14 +23,13 @@
#include <string.h>
#include <crm/crm.h>
-#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
-#include <crm/fencing/internal.h>
+#include <crm/fencing/internal.h> // stonith__register_messages()
#include <crm/cib.h>
#include <crm/pengine/status.h>
@@ -53,7 +52,7 @@ struct {
stonith_key_value_t *params;
int fence_level;
int timeout ;
- int tolerance;
+ long long tolerance_ms;
int delay;
char *agent;
char *confirm_host;
@@ -265,7 +264,15 @@ add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data,
gboolean
add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
- options.tolerance = crm_get_msec(optarg) / 1000;
+ // pcmk__request_fencing() expects an unsigned int
+ options.tolerance_ms = crm_get_msec(optarg);
+
+ if (options.tolerance_ms < 0) {
+ crm_warn("Ignoring invalid tolerance '%s'", optarg);
+ options.tolerance_ms = 0;
+ } else {
+ options.tolerance_ms = QB_MIN(options.tolerance_ms, UINT_MAX);
+ }
return TRUE;
}
@@ -339,8 +346,8 @@ request_fencing(stonith_t *st, const char *target, const char *command,
char *reason = NULL;
int rc = pcmk__request_fencing(st, target, command, name,
options.timeout * 1000,
- options.tolerance * 1000,
- options.delay, &reason);
+ options.tolerance_ms, options.delay,
+ &reason);
if (rc != pcmk_rc_ok) {
const char *rc_str = pcmk_rc_str(rc);
@@ -408,6 +415,8 @@ main(int argc, char **argv)
goto done;
}
+ pcmk__output_enable_list_element(out);
+
stonith__register_messages(out);
if (args->version) {
diff --git a/xml/Makefile.am b/xml/Makefile.am
index 6acb338..670a856 100644
--- a/xml/Makefile.am
+++ b/xml/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.
#
@@ -63,6 +63,7 @@ API_request_base = command-output \
crm_rule \
crm_shadow \
crm_simulate \
+ crm_ticket \
crmadmin \
digests \
pacemakerd \
@@ -90,10 +91,13 @@ API_base = $(API_request_base) \
node-attrs \
node-history \
nodes \
+ ocf-ra \
+ options \
patchset \
resources \
status \
- subprocess-output
+ subprocess-output \
+ ticket
CIB_base = cib \
$(CIB_cfg_base) \
@@ -162,9 +166,9 @@ nodist_CIB_DATA = $(CIB_generated)
nodist_MON_DATA = $(MON_generated)
EXTRA_DIST = README.md \
- best-match.sh \
cibtr-2.rng \
context-of.xsl \
+ rng-helper \
ocf-meta2man.xsl \
regression.sh \
upgrade-2.10-roundtrip.xsl \
@@ -191,22 +195,9 @@ api/api-result.rng: api/api-result-$(API_max).rng
$(AM_V_at)$(MKDIR_P) api # might not exist in VPATH build
$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
-api/api-result-%.rng: $(API_build_copies) best-match.sh Makefile.am
- $(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
- $(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
- $(AM_V_at)echo ' <start>' >> $@
- $(AM_V_at)echo ' <element name="pacemaker-result">' >> $@
- $(AM_V_at)echo ' <attribute name="api-version"> <text /> </attribute>' >> $@
- $(AM_V_at)echo ' <attribute name="request"> <text /> </attribute>' >> $@
- $(AM_V_at)echo ' <optional>' >> $@
- $(AM_V_at)echo ' <choice>' >> $@
- $(AM_V_at)for rng in $(API_request_base); do $(srcdir)/best-match.sh api/$$rng $(*) $(@) " " || :; done
- $(AM_V_at)echo ' </choice>' >> $@
- $(AM_V_at)echo ' </optional>' >> $@
- $(AM_V_at)$(srcdir)/best-match.sh api/status $(*) $(@) " " || :
- $(AM_V_at)echo ' </element>' >> $@
- $(AM_V_at)echo ' </start>' >> $@
- $(AM_V_SCHEMA)echo '</grammar>' >> $@
+api/api-result-%.rng: $(API_build_copies) rng-helper Makefile.am
+ $(AM_V_SCHEMA)$(builddir)/rng-helper build_api_rng "$@" "$*" \
+ $(API_request_base)
crm_mon.rng: api/crm_mon-$(MON_max).rng
$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
@@ -227,27 +218,15 @@ crm_mon.rng: api/crm_mon-$(MON_max).rng
pacemaker.rng: pacemaker-$(CIB_max).rng
$(AM_V_SCHEMA)cp $(top_builddir)/xml/$< $@
-pacemaker-%.rng: $(CIB_build_copies) best-match.sh Makefile.am
- $(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
- $(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
- $(AM_V_at)echo ' <start>' >> $@
- $(AM_V_at)echo ' <element name="cib">' >> $@
- $(AM_V_at)$(srcdir)/best-match.sh cib $(*) $(@) " "
- $(AM_V_at)echo ' <element name="configuration">' >> $@
- $(AM_V_at)echo ' <interleave>' >> $@
- $(AM_V_at)for rng in $(CIB_cfg_base); do $(srcdir)/best-match.sh $$rng $(*) $(@) " " || :; done
- $(AM_V_at)echo ' </interleave>' >> $@
- $(AM_V_at)echo ' </element>' >> $@
- $(AM_V_at)echo ' <optional>' >> $@
- $(AM_V_at)echo ' <element name="status">' >> $@
- $(AM_V_at)$(srcdir)/best-match.sh status $(*) $(@) " "
- $(AM_V_at)echo ' </element>' >> $@
- $(AM_V_at)echo ' </optional>' >> $@
- $(AM_V_at)echo ' </element>' >> $@
- $(AM_V_at)echo ' </start>' >> $@
- $(AM_V_SCHEMA)echo '</grammar>' >> $@
+pacemaker-%.rng: $(CIB_build_copies) rng-helper Makefile.am
+ $(AM_V_SCHEMA)$(builddir)/rng-helper build_cib_rng "$@" "$*" \
+ $(CIB_cfg_base)
# Dynamically generated CIB schema listing all pacemaker versions
+#
+# @COMPAT none, pacemaker-0.6, pacemaker-0.7, pacemaker-1.1, and
+# transitional-0.6 are deprecated since 2.1.8, as is validate-with being
+# optional
versions.rng: pacemaker-$(CIB_max).rng Makefile.am
$(AM_V_at)echo '<?xml version="1.0" encoding="UTF-8"?>' > $@
$(AM_V_at)echo '<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">' >> $@
@@ -273,14 +252,14 @@ versions.rng: pacemaker-$(CIB_max).rng Makefile.am
$(AM_V_SCHEMA)echo '</grammar>' >> $@
.PHONY: diff
-diff: best-match.sh
+diff: rng-helper
@echo "# Comparing changes in + since $(CIB_max)"
- @./version-diff.sh ${CIB_version_pairs_last}
+ @$(builddir)/rng-helper diff ${CIB_version_pairs_last}
.PHONY: fulldiff
-fulldiff: best-match.sh
+fulldiff: rng-helper
@echo "# Comparing all changes across all the subsequent increments"
- @./version-diff.sh ${CIB_version_pairs}
+ @$(builddir)/rng-helper diff ${CIB_version_pairs}
CLEANFILES = $(API_generated) \
$(CIB_generated) \
diff --git a/xml/README.md b/xml/README.md
index a3a1973..1fbd42c 100644
--- a/xml/README.md
+++ b/xml/README.md
@@ -20,6 +20,8 @@ A versioned schema offers transparent backward and forward compatibility.
| Pacemaker | Latest Schema | Changed
| --------- | ------------- | ----------------------------------------------
+| `2.1.8` | `3.10` | `alerts`, `constraints`, `nodes`, `nvset`,
+| | | `options`, `resources`, `rule`
| `2.1.5` | `3.9` | `alerts`, `constraints`, `nodes`, `nvset`,
| | | `options`, `resources`, `rule`
| `2.1.3` | `3.8` | `acls`
diff --git a/xml/alerts-3.10.rng b/xml/alerts-3.10.rng
new file mode 100644
index 0000000..6c58625
--- /dev/null
+++ b/xml/alerts-3.10.rng
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-alerts"/>
+ </start>
+
+ <define name="element-alerts">
+ <optional>
+ <element name="alerts">
+ <zeroOrMore>
+ <element name="alert">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <!-- path to the script called for alert -->
+ <attribute name="path"><text/></attribute>
+ <interleave>
+ <ref name="element-alert-extra"/>
+ <optional>
+ <element name="select">
+ <interleave>
+ <optional>
+ <element name="select_attributes">
+ <zeroOrMore>
+ <element name="attribute">
+ <attribute name="id"><data type="ID"/></attribute>
+ <attribute name="name"><text/></attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="select_fencing">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="select_nodes">
+ <empty/>
+ </element>
+ </optional>
+ <optional>
+ <element name="select_resources">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <element name="recipient">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <attribute name="value"><text/></attribute>
+ <ref name="element-alert-extra"/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </define>
+
+ <define name="element-alert-extra">
+ <zeroOrMore>
+ <choice>
+ <element name="meta_attributes">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ <element name="instance_attributes">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+</grammar>
diff --git a/xml/api/crm_attribute-2.34.rng b/xml/api/crm_attribute-2.34.rng
new file mode 100644
index 0000000..8e84f0b
--- /dev/null
+++ b/xml/api/crm_attribute-2.34.rng
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm_attribute" />
+ </start>
+
+ <define name="element-crm_attribute">
+ <choice>
+ <zeroOrMore>
+ <ref name="element-attribute" />
+ </zeroOrMore>
+ <externalRef href="options-2.34.rng" />
+ </choice>
+ </define>
+
+ <define name="element-attribute">
+ <element name="attribute">
+ <optional>
+ <attribute name="scope"> <text /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="id"> <text /> </attribute>
+ </optional>
+ <attribute name="name"> <text /> </attribute>
+ <attribute name="value"> <text /> </attribute>
+ <optional>
+ <attribute name="host"> <text /> </attribute>
+ </optional>
+ </element>
+ </define>
+</grammar>
diff --git a/xml/api/crm_attribute-2.36.rng b/xml/api/crm_attribute-2.36.rng
new file mode 100644
index 0000000..2093a7d
--- /dev/null
+++ b/xml/api/crm_attribute-2.36.rng
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm_attribute" />
+ </start>
+
+ <define name="element-crm_attribute">
+ <choice>
+ <zeroOrMore>
+ <ref name="element-attribute" />
+ </zeroOrMore>
+ <externalRef href="options-2.36.rng" />
+ </choice>
+ </define>
+
+ <define name="element-attribute">
+ <element name="attribute">
+ <optional>
+ <attribute name="scope"> <text /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="id"> <text /> </attribute>
+ </optional>
+ <attribute name="name"> <text /> </attribute>
+ <attribute name="value"> <text /> </attribute>
+ <optional>
+ <attribute name="host"> <text /> </attribute>
+ </optional>
+ </element>
+ </define>
+</grammar>
diff --git a/xml/api/crm_mon-2.35.rng b/xml/api/crm_mon-2.35.rng
new file mode 100644
index 0000000..78ec973
--- /dev/null
+++ b/xml/api/crm_mon-2.35.rng
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm-mon"/>
+ </start>
+
+ <define name="element-crm-mon">
+ <choice>
+ <ref name="element-crm-mon-disconnected" />
+ <group>
+ <optional>
+ <externalRef href="pacemakerd-health-2.25.rng" />
+ </optional>
+ <optional>
+ <ref name="element-summary" />
+ </optional>
+ <optional>
+ <ref name="nodes-list" />
+ </optional>
+ <optional>
+ <ref name="resources-list" />
+ </optional>
+ <optional>
+ <ref name="node-attributes-list" />
+ </optional>
+ <optional>
+ <externalRef href="node-history-2.12.rng"/>
+ </optional>
+ <optional>
+ <ref name="failures-list" />
+ </optional>
+ <optional>
+ <ref name="fence-event-list" />
+ </optional>
+ <optional>
+ <ref name="tickets-list" />
+ </optional>
+ <optional>
+ <ref name="bans-list" />
+ </optional>
+ </group>
+ </choice>
+ </define>
+
+ <define name="element-crm-mon-disconnected">
+ <element name="crm-mon-disconnected">
+ <optional>
+ <attribute name="description"> <text /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="pacemakerd-state"> <text /> </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-summary">
+ <element name="summary">
+ <optional>
+ <element name="stack">
+ <attribute name="type"> <text /> </attribute>
+ <optional>
+ <attribute name="pacemakerd-state">
+ <text />
+ </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="current_dc">
+ <attribute name="present"> <data type="boolean" /> </attribute>
+ <optional>
+ <group>
+ <attribute name="version"> <text /> </attribute>
+ <attribute name="name"> <text /> </attribute>
+ <attribute name="id"> <text /> </attribute>
+ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
+ </group>
+ </optional>
+ <optional>
+ <attribute name="mixed_version"> <data type="boolean" /> </attribute>
+ </optional>
+ </element>
+ </optional>
+ <optional>
+ <element name="last_update">
+ <attribute name="time"> <text /> </attribute>
+ <optional>
+ <attribute name="origin"> <text /> </attribute>
+ </optional>
+ </element>
+ <element name="last_change">
+ <attribute name="time"> <text /> </attribute>
+ <attribute name="user"> <text /> </attribute>
+ <attribute name="client"> <text /> </attribute>
+ <attribute name="origin"> <text /> </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="nodes_configured">
+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
+ </element>
+ <element name="resources_configured">
+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
+ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
+ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
+ </element>
+ </optional>
+ <optional>
+ <element name="cluster_options">
+ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
+ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
+ <attribute name="no-quorum-policy"> <text /> </attribute>
+ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
+ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
+ <attribute name="stonith-timeout-ms"> <data type="integer" /> </attribute>
+ <attribute name="priority-fencing-delay-ms"> <data type="integer" /> </attribute>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="resources-list">
+ <element name="resources">
+ <zeroOrMore>
+ <externalRef href="resources-2.29.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="nodes-list">
+ <element name="nodes">
+ <zeroOrMore>
+ <externalRef href="nodes-2.29.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="node-attributes-list">
+ <element name="node_attributes">
+ <zeroOrMore>
+ <externalRef href="node-attrs-2.8.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="failures-list">
+ <element name="failures">
+ <zeroOrMore>
+ <externalRef href="failure-2.8.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="fence-event-list">
+ <element name="fence_history">
+ <optional>
+ <attribute name="status"> <data type="integer" /> </attribute>
+ </optional>
+ <zeroOrMore>
+ <externalRef href="fence-event-2.15.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="tickets-list">
+ <element name="tickets">
+ <zeroOrMore>
+ <externalRef href="ticket-2.35.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="bans-list">
+ <element name="bans">
+ <zeroOrMore>
+ <ref name="element-ban" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="element-ban">
+ <element name="ban">
+ <attribute name="id"> <text /> </attribute>
+ <attribute name="resource"> <text /> </attribute>
+ <attribute name="node"> <text /> </attribute>
+ <attribute name="weight"> <data type="integer" /> </attribute>
+ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
+ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
+ provided solely for API backward compatibility. It will be
+ removed in a future release. Check promoted-only instead.
+ -->
+ <attribute name="master_only"> <data type="boolean" /> </attribute>
+ </element>
+ </define>
+</grammar>
diff --git a/xml/api/crm_resource-2.36.rng b/xml/api/crm_resource-2.36.rng
new file mode 100644
index 0000000..5050364
--- /dev/null
+++ b/xml/api/crm_resource-2.36.rng
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm-resource"/>
+ </start>
+
+ <define name="element-crm-resource">
+ <choice>
+ <ref name="agents-list" />
+ <ref name="alternatives-list" />
+ <ref name="constraints-list" />
+ <externalRef href="generic-list-2.4.rng"/>
+ <element name="metadata"> <text/> </element>
+ <ref name="locate-list" />
+ <ref name="operations-list" />
+ <externalRef href="options-2.36.rng"/>
+ <ref name="providers-list" />
+ <ref name="reasons-list" />
+ <ref name="resource-check" />
+ <ref name="resource-config" />
+ <ref name="resources-list" />
+ <ref name="resource-agent-action" />
+ </choice>
+ </define>
+
+ <define name="agents-list">
+ <element name="agents">
+ <attribute name="standard"> <text/> </attribute>
+ <optional>
+ <attribute name="provider"> <text/> </attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="agent"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="alternatives-list">
+ <element name="providers">
+ <attribute name="for"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="provider"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="constraints-list">
+ <element name="constraints">
+ <interleave>
+ <zeroOrMore>
+ <ref name="rsc-location" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="rsc-colocation" />
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="locate-list">
+ <element name="nodes">
+ <attribute name="resource"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="node">
+ <optional>
+ <attribute name="state"><value>promoted</value></attribute>
+ </optional>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="rsc-location">
+ <element name="rsc_location">
+ <attribute name="node"> <text/> </attribute>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <externalRef href="../score.rng"/>
+ </element>
+ </define>
+
+ <define name="operations-list">
+ <element name="operations">
+ <oneOrMore>
+ <ref name="element-operation-list" />
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="providers-list">
+ <element name="providers">
+ <attribute name="standard"> <value>ocf</value> </attribute>
+ <optional>
+ <attribute name="agent"> <text/> </attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="provider"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="reasons-list">
+ <element name="reason">
+ <!-- set only when resource and node are both specified -->
+ <optional>
+ <attribute name="running_on"> <text/> </attribute>
+ </optional>
+
+ <!-- set only when only a resource is specified -->
+ <optional>
+ <attribute name="running"> <data type="boolean"/> </attribute>
+ </optional>
+
+ <choice>
+ <ref name="reasons-with-no-resource"/>
+ <ref name="resource-check"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="reasons-with-no-resource">
+ <element name="resources">
+ <zeroOrMore>
+ <element name="resource">
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="running"> <data type="boolean"/> </attribute>
+ <optional>
+ <attribute name="host"> <text/> </attribute>
+ </optional>
+ <ref name="resource-check"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="resource-config">
+ <element name="resource_config">
+ <externalRef href="resources-2.29.rng" />
+ <element name="xml"> <text/> </element>
+ </element>
+ </define>
+
+ <define name="resource-check">
+ <element name="check">
+ <attribute name="id"> <text/> </attribute>
+ <optional>
+ <choice>
+ <attribute name="remain_stopped"><value>true</value></attribute>
+ <attribute name="promotable"><value>false</value></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="unmanaged"><value>true</value></attribute>
+ </optional>
+ <optional>
+ <attribute name="locked-to"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="unhealthy"><value>true</value></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="resources-list">
+ <element name="resources">
+ <zeroOrMore>
+ <externalRef href="resources-2.29.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="rsc-colocation">
+ <element name="rsc_colocation">
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="with-rsc"> <text/> </attribute>
+ <externalRef href="../score.rng"/>
+ <optional>
+ <attribute name="node-attribute"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="with-rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-operation-list">
+ <element name="operation">
+ <optional>
+ <group>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="agent"> <text/> </attribute>
+ </group>
+ </optional>
+ <attribute name="op"> <text/> </attribute>
+ <attribute name="node"> <text/> </attribute>
+ <attribute name="call"> <data type="integer" /> </attribute>
+ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
+ <optional>
+ <attribute name="last-rc-change"> <text/> </attribute>
+ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
+ </optional>
+ <attribute name="status"> <text/> </attribute>
+ </element>
+ </define>
+
+ <define name="resource-agent-action">
+ <element name="resource-agent-action">
+ <attribute name="action"> <text/> </attribute>
+ <optional>
+ <attribute name="rsc"> <text/> </attribute>
+ </optional>
+ <attribute name="class"> <text/> </attribute>
+ <attribute name="type"> <text/> </attribute>
+ <optional>
+ <attribute name="provider"> <text/> </attribute>
+ </optional>
+ <optional>
+ <ref name="overrides-list"/>
+ </optional>
+ <ref name="agent-status"/>
+ <optional>
+ <element name="command">
+ <choice>
+ <text />
+ <externalRef href="subprocess-output-2.23.rng"/>
+ </choice>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="overrides-list">
+ <element name="overrides">
+ <zeroOrMore>
+ <element name="override">
+ <optional>
+ <attribute name="rsc"> <text/> </attribute>
+ </optional>
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="value"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="agent-status">
+ <element name="agent-status">
+ <attribute name="code"> <data type="integer" /> </attribute>
+ <optional>
+ <attribute name="message"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="execution_code"> <data type="integer" /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="execution_message"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="reason"> <text/> </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="attribute-roles">
+ <choice>
+ <value>Stopped</value>
+ <value>Started</value>
+ <value>Promoted</value>
+ <value>Unpromoted</value>
+
+ <!-- These synonyms for Promoted/Unpromoted are allowed for
+ backward compatibility with output from older Pacemaker
+ versions that used them -->
+ <value>Master</value>
+ <value>Slave</value>
+ </choice>
+ </define>
+</grammar>
diff --git a/xml/api/crm_resource-2.37.rng b/xml/api/crm_resource-2.37.rng
new file mode 100644
index 0000000..d242b85
--- /dev/null
+++ b/xml/api/crm_resource-2.37.rng
@@ -0,0 +1,348 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm-resource"/>
+ </start>
+
+ <define name="element-crm-resource">
+ <choice>
+ <ref name="agents-list" />
+ <ref name="alternatives-list" />
+ <ref name="constraints-list" />
+ <externalRef href="generic-list-2.4.rng"/>
+ <element name="metadata"> <text/> </element>
+ <ref name="locate-list" />
+ <ref name="operations-list" />
+ <externalRef href="options-2.36.rng"/>
+ <ref name="providers-list" />
+ <ref name="reasons-list" />
+ <ref name="resource-check" />
+ <ref name="resource-config" />
+ <ref name="resources-list" />
+ <ref name="resource-agent-action" />
+ <ref name="resource-settings-list" />
+ </choice>
+ </define>
+
+ <define name="agents-list">
+ <element name="agents">
+ <attribute name="standard"> <text/> </attribute>
+ <optional>
+ <attribute name="provider"> <text/> </attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="agent"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="alternatives-list">
+ <element name="providers">
+ <attribute name="for"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="provider"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="constraints-list">
+ <element name="constraints">
+ <interleave>
+ <zeroOrMore>
+ <ref name="rsc-location" />
+ </zeroOrMore>
+ <zeroOrMore>
+ <ref name="rsc-colocation" />
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="locate-list">
+ <element name="nodes">
+ <attribute name="resource"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="node">
+ <optional>
+ <attribute name="state"><value>promoted</value></attribute>
+ </optional>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="rsc-location">
+ <element name="rsc_location">
+ <attribute name="node"> <text/> </attribute>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <externalRef href="../score.rng"/>
+ </element>
+ </define>
+
+ <define name="operations-list">
+ <element name="operations">
+ <oneOrMore>
+ <ref name="element-operation-list" />
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="providers-list">
+ <element name="providers">
+ <attribute name="standard"> <value>ocf</value> </attribute>
+ <optional>
+ <attribute name="agent"> <text/> </attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="provider"> <text/> </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="reasons-list">
+ <element name="reason">
+ <!-- set only when resource and node are both specified -->
+ <optional>
+ <attribute name="running_on"> <text/> </attribute>
+ </optional>
+
+ <!-- set only when only a resource is specified -->
+ <optional>
+ <attribute name="running"> <data type="boolean"/> </attribute>
+ </optional>
+
+ <choice>
+ <ref name="reasons-with-no-resource"/>
+ <ref name="resource-check"/>
+ </choice>
+ </element>
+ </define>
+
+ <define name="reasons-with-no-resource">
+ <element name="resources">
+ <zeroOrMore>
+ <element name="resource">
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="running"> <data type="boolean"/> </attribute>
+ <optional>
+ <attribute name="host"> <text/> </attribute>
+ </optional>
+ <ref name="resource-check"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="resource-config">
+ <element name="resource_config">
+ <externalRef href="resources-2.29.rng" />
+ <element name="xml"> <text/> </element>
+ </element>
+ </define>
+
+ <define name="resource-check">
+ <element name="check">
+ <attribute name="id"> <text/> </attribute>
+ <optional>
+ <choice>
+ <attribute name="remain_stopped"><value>true</value></attribute>
+ <attribute name="promotable"><value>false</value></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="unmanaged"><value>true</value></attribute>
+ </optional>
+ <optional>
+ <attribute name="locked-to"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="unhealthy"><value>true</value></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="resources-list">
+ <element name="resources">
+ <zeroOrMore>
+ <externalRef href="resources-2.29.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="rsc-colocation">
+ <element name="rsc_colocation">
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="with-rsc"> <text/> </attribute>
+ <externalRef href="../score.rng"/>
+ <optional>
+ <attribute name="node-attribute"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="with-rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-operation-list">
+ <element name="operation">
+ <optional>
+ <group>
+ <attribute name="rsc"> <text/> </attribute>
+ <attribute name="agent"> <text/> </attribute>
+ </group>
+ </optional>
+ <attribute name="op"> <text/> </attribute>
+ <attribute name="node"> <text/> </attribute>
+ <attribute name="call"> <data type="integer" /> </attribute>
+ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
+ <optional>
+ <attribute name="last-rc-change"> <text/> </attribute>
+ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
+ </optional>
+ <attribute name="status"> <text/> </attribute>
+ </element>
+ </define>
+
+ <define name="resource-agent-action">
+ <element name="resource-agent-action">
+ <attribute name="action"> <text/> </attribute>
+ <optional>
+ <attribute name="rsc"> <text/> </attribute>
+ </optional>
+ <attribute name="class"> <text/> </attribute>
+ <attribute name="type"> <text/> </attribute>
+ <optional>
+ <attribute name="provider"> <text/> </attribute>
+ </optional>
+ <optional>
+ <ref name="overrides-list"/>
+ </optional>
+ <ref name="agent-status"/>
+ <optional>
+ <element name="command">
+ <choice>
+ <text />
+ <externalRef href="subprocess-output-2.23.rng"/>
+ </choice>
+ </element>
+ </optional>
+ </element>
+ </define>
+
+ <define name="resource-settings-list">
+ <element name="resource-settings">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-bundle-settings"/>
+ <ref name="element-clone-settings"/>
+ <ref name="element-group-settings"/>
+ <ref name="element-primitive-settings"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="element-bundle-settings">
+ <element name="bundle">
+ <ref name="element-resource-setting-attrs" />
+ </element>
+ </define>
+
+ <define name="element-clone-settings">
+ <element name="clone">
+ <ref name="element-resource-setting-attrs" />
+ </element>
+ </define>
+
+ <define name="element-group-settings">
+ <element name="group">
+ <ref name="element-resource-setting-attrs" />
+ </element>
+ </define>
+
+ <define name="element-primitive-settings">
+ <element name="primitive">
+ <ref name="element-resource-setting-attrs" />
+ </element>
+ </define>
+
+ <define name="element-resource-setting-attrs">
+ <attribute name="id"> <data type="ID"/> </attribute>
+ <interleave>
+ <optional>
+ <element name="meta_attributes">
+ <externalRef href="../nvset-3.10.rng" />
+ </element>
+ </optional>
+ <optional>
+ <element name="instance_attributes">
+ <externalRef href="../nvset-3.10.rng" />
+ </element>
+ </optional>
+ <optional>
+ <element name="utilization">
+ <externalRef href="../nvset-3.10.rng" />
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+ <define name="overrides-list">
+ <element name="overrides">
+ <zeroOrMore>
+ <element name="override">
+ <optional>
+ <attribute name="rsc"> <text/> </attribute>
+ </optional>
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="value"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="agent-status">
+ <element name="agent-status">
+ <attribute name="code"> <data type="integer" /> </attribute>
+ <optional>
+ <attribute name="message"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="execution_code"> <data type="integer" /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="execution_message"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="reason"> <text/> </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="attribute-roles">
+ <choice>
+ <value>Stopped</value>
+ <value>Started</value>
+ <value>Promoted</value>
+ <value>Unpromoted</value>
+
+ <!-- These synonyms for Promoted/Unpromoted are allowed for
+ backward compatibility with output from older Pacemaker
+ versions that used them -->
+ <value>Master</value>
+ <value>Slave</value>
+ </choice>
+ </define>
+</grammar>
diff --git a/xml/api/crm_ticket-2.35.rng b/xml/api/crm_ticket-2.35.rng
new file mode 100644
index 0000000..b2d72f6
--- /dev/null
+++ b/xml/api/crm_ticket-2.35.rng
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-crm_ticket"/>
+ </start>
+
+ <define name="element-crm_ticket">
+ <ref name="tickets-list" />
+ <optional>
+ <ref name="resources-list" />
+ </optional>
+ </define>
+
+ <define name="resources-list">
+ <element name="resources">
+ <zeroOrMore>
+ <element name="resource">
+ <attribute name="id"> <data type="ID" /> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="tickets-list">
+ <element name="tickets">
+ <zeroOrMore>
+ <externalRef href="ticket-2.35.rng" />
+ </zeroOrMore>
+ </element>
+ </define>
+
+</grammar>
diff --git a/xml/api/ocf-ra-1.1.rng b/xml/api/ocf-ra-1.1.rng
new file mode 100644
index 0000000..5186d9f
--- /dev/null
+++ b/xml/api/ocf-ra-1.1.rng
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <!--
+ Version 1.1 of the OCF resource agent API schema
+ Adapted from https://github.com/ClusterLabs/OCF-spec
+ -->
+ <start>
+ <ref name="element-resource-agent" />
+ </start>
+
+ <define name="element-resource-agent">
+ <element name="resource-agent">
+ <attribute name="name" />
+ <optional>
+ <attribute name="version" />
+ </optional>
+
+ <element name="version"> <text /> </element>
+
+ <zeroOrMore>
+ <ref name="element-longdesc" />
+ </zeroOrMore>
+
+ <zeroOrMore>
+ <ref name="element-shortdesc" />
+ </zeroOrMore>
+
+ <ref name="element-parameters" />
+ <ref name="element-actions" />
+ <optional>
+ <ref name="element-special" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-parameters">
+ <element name="parameters">
+ <oneOrMore>
+ <ref name="element-parameter" />
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="element-parameter">
+ <element name="parameter">
+ <ref name="parameter-attributes" />
+
+ <optional>
+ <ref name="element-deprecated" />
+ </optional>
+
+ <oneOrMore>
+ <ref name="element-longdesc" />
+ </oneOrMore>
+
+ <oneOrMore>
+ <ref name="element-shortdesc" />
+ </oneOrMore>
+
+ <ref name="element-content" />
+ </element>
+ </define>
+
+ <define name="parameter-attributes">
+ <attribute name="name" />
+ <optional>
+ <attribute name="unique-group" />
+ </optional>
+ <optional>
+ <!-- "unique" is deprecated -->
+ <attribute name="unique">
+ <ref name="boolean-values" />
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="required">
+ <ref name="boolean-values" />
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="reloadable">
+ <ref name="boolean-values" />
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="boolean-values">
+ <choice>
+ <value>0</value>
+ <value>1</value>
+ </choice>
+ </define>
+
+ <define name="element-deprecated">
+ <element name="deprecated">
+ <empty />
+ <interleave>
+ <zeroOrMore>
+ <element name="replaced-with">
+ <attribute name="name" />
+ </element>
+ </zeroOrMore>
+ <zeroOrMore>
+ <element name="desc">
+ <ref name="description" />
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-longdesc">
+ <element name="longdesc">
+ <ref name="description" />
+ </element>
+ </define>
+
+ <define name="element-shortdesc">
+ <element name="shortdesc">
+ <ref name="description" />
+ </element>
+ </define>
+
+ <define name="description">
+ <attribute name="lang" />
+ <ref name="anyElement" />
+ </define>
+
+ <define name="element-content">
+ <element name="content">
+ <choice>
+ <attribute name="type">
+ <ref name="atomic-types" />
+ </attribute>
+ <group>
+ <attribute name="type">
+ <value>select</value>
+ </attribute>
+ <oneOrMore>
+ <element name="option">
+ <attribute name="value" />
+ </element>
+ </oneOrMore>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="default" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="atomic-types">
+ <choice>
+ <value>boolean</value>
+ <value>string</value>
+ <value>integer</value>
+ </choice>
+ </define>
+
+ <define name="element-actions">
+ <element name="actions">
+ <oneOrMore>
+ <ref name="element-action" />
+ </oneOrMore>
+ </element>
+ </define>
+
+ <define name="element-action">
+ <element name="action">
+ <attribute name="name" />
+ <attribute name="timeout" />
+ <optional>
+ <attribute name="interval" />
+ </optional>
+ <optional>
+ <attribute name="start-delay" />
+ </optional>
+ <optional>
+ <attribute name="depth" />
+ </optional>
+ <optional>
+ <attribute name="role" />
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-special">
+ <element name="special">
+ <attribute name="tag" />
+ <ref name="anyElement" />
+ </element>
+ </define>
+
+ <define name="anyElement">
+ <zeroOrMore>
+ <choice>
+ <text/>
+ <element>
+ <anyName/>
+ <ref name="any" />
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+ <define name="any">
+ <zeroOrMore>
+ <choice>
+ <attribute><anyName /></attribute>
+ <text/>
+ <element>
+ <anyName/>
+ <ref name="any" />
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+</grammar>
diff --git a/xml/api/options-2.34.rng b/xml/api/options-2.34.rng
new file mode 100644
index 0000000..94d3aad
--- /dev/null
+++ b/xml/api/options-2.34.rng
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <!--
+ Pacemaker outputs option lists in an OCF-like XML format, but there are
+ differences that break validation against the unaltered schema.
+ -->
+
+ <include href="ocf-ra-1.1.rng">
+ <!--
+ The OCF RA 1.1 schema requires an actions element. Pacemaker option
+ lists don't include an actions element.
+ -->
+ <define name="element-actions">
+ <empty />
+ </define>
+ </include>
+
+ <!-- Extend the allowed types to include percentage and time -->
+ <define name="atomic-types" combine="choice">
+ <choice>
+ <value>percentage</value>
+ <value>time</value>
+ </choice>
+ </define>
+
+ <!-- Pacemaker options have additional parameter attributes -->
+ <define name="parameter-attributes" combine="interleave">
+ <!-- Parameter is for advanced use only -->
+ <attribute name="advanced" />
+
+ <!-- Parameter's value is Pacemaker-generated, not user-configured -->
+ <attribute name="generated" />
+ </define>
+
+</grammar>
diff --git a/xml/api/options-2.36.rng b/xml/api/options-2.36.rng
new file mode 100644
index 0000000..280e398
--- /dev/null
+++ b/xml/api/options-2.36.rng
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <!--
+ Pacemaker outputs option lists in an OCF-like XML format, but there are
+ differences that break validation against the unaltered schema.
+ -->
+
+ <include href="ocf-ra-1.1.rng">
+ <!--
+ The OCF RA 1.1 schema requires an actions element. Pacemaker option
+ lists don't include an actions element.
+ -->
+ <define name="element-actions">
+ <empty />
+ </define>
+ </include>
+
+ <!-- Extend the allowed types (see Pacemaker Explained for details) -->
+ <define name="atomic-types" combine="choice">
+ <choice>
+ <value>duration</value>
+ <value>epoch_time</value>
+ <value>nonnegative_integer</value>
+ <value>percentage</value>
+ <value>port</value>
+ <value>score</value>
+ <value>timeout</value>
+ <value>version</value>
+
+ <!-- @COMPAT Deprecated -->
+ <value>time</value>
+ </choice>
+ </define>
+
+ <!-- Pacemaker options have additional parameter attributes -->
+ <define name="parameter-attributes" combine="interleave">
+ <!-- Parameter is for advanced use only -->
+ <attribute name="advanced" />
+
+ <!-- Parameter's value is Pacemaker-generated, not user-configured -->
+ <attribute name="generated" />
+ </define>
+
+</grammar>
diff --git a/xml/api/stonith_admin-2.33.rng b/xml/api/stonith_admin-2.33.rng
new file mode 100644
index 0000000..3cf63fb
--- /dev/null
+++ b/xml/api/stonith_admin-2.33.rng
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="element-stonith-admin"/>
+ </start>
+
+ <define name="element-stonith-admin">
+ <choice>
+ <ref name="stonith-admin-list" />
+ <ref name="element-last-fenced" />
+ <ref name="element-validation" />
+ <element name="metadata"> <text /> </element>
+ </choice>
+ </define>
+
+ <define name="stonith-admin-list">
+ <optional>
+ <element name="list">
+ <attribute name="name"> <text /> </attribute>
+ <attribute name="count"> <data type="nonNegativeInteger" /> </attribute>
+ <choice>
+ <empty/>
+ <oneOrMore>
+ <externalRef href="item-1.1.rng"/>
+ </oneOrMore>
+ <oneOrMore>
+ <externalRef href="fence-event-2.15.rng"/>
+ </oneOrMore>
+ </choice>
+ </element>
+ </optional>
+ </define>
+
+ <define name="element-last-fenced">
+ <element name="last-fenced">
+ <attribute name="target"> <text /> </attribute>
+ <attribute name="when"> <text /> </attribute>
+ </element>
+ </define>
+
+ <define name="element-validation">
+ <element name="validate">
+ <attribute name="agent"> <text /> </attribute>
+ <attribute name="valid"> <data type="boolean" /> </attribute>
+ <optional>
+ <attribute name="device"> <text /> </attribute>
+ </optional>
+ <optional>
+ <externalRef href="command-output-2.23.rng" />
+ </optional>
+ </element>
+ </define>
+</grammar>
diff --git a/xml/api/ticket-2.35.rng b/xml/api/ticket-2.35.rng
new file mode 100644
index 0000000..017ef38
--- /dev/null
+++ b/xml/api/ticket-2.35.rng
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <include href="../constraints-3.9.rng">
+ <start>
+ <ref name="element-ticket"/>
+ </start>
+
+ <!-- This redefines the constraints element from constraints-X.X.rng
+ so it can only contain the element-rsc_ticket element. This allows
+ us to restrict it so only those kinds of constraints are allowed
+ in ticket XML.
+ -->
+ <define name="element-constraints">
+ <element name="constraints">
+ <zeroOrMore>
+ <ref name="element-rsc_ticket"/>
+ </zeroOrMore>
+ </element>
+ </define>
+ </include>
+
+ <define name="element-ticket">
+ <element name="ticket">
+ <attribute name="id"> <text /> </attribute>
+ <optional>
+ <attribute name="status">
+ <choice>
+ <value>granted</value>
+ <value>revoked</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="standby"> <data type="boolean" /> </attribute>
+ </optional>
+ <optional>
+ <attribute name="last-granted"> <text /> </attribute>
+ </optional>
+ <optional>
+ <element name="attribute">
+ <attribute name="name"> <text /> </attribute>
+ <attribute name="value"> <text /> </attribute>
+ </element>
+ </optional>
+ <zeroOrMore>
+ <attribute>
+ <anyName>
+ <except>
+ <name>attribute</name>
+ <name>constraints</name>
+ <name>id</name>
+ <name>last-granted</name>
+ <name>standby</name>
+ <name>status</name>
+ </except>
+ </anyName>
+ <text />
+ </attribute>
+ </zeroOrMore>
+ <optional>
+ <ref name="element-constraints" />
+ </optional>
+ </element>
+ </define>
+
+</grammar>
diff --git a/xml/best-match.sh b/xml/best-match.sh
deleted file mode 100755
index 580c29f..0000000
--- a/xml/best-match.sh
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/bin/sh
-#
-# Find the (sub-)schema that best matches a desired version.
-#
-# Version numbers are assumed to be in the format X.Y,
-# where X and Y are integers, and Y is no more than 3 digits,
-# or the special value "next".
-#
-
-# (Sub-)schema name (e.g. "resources")
-base="$1"; shift
-
-# Desired version (e.g. "1.0" or "next")
-target="$1"; shift
-
-# If not empty, append the best match as an XML externalRef to this file
-# (otherwise, just echo the best match). Using readlink allows building
-# from a different directory.
-destination="$(readlink -f "$1")"; shift
-
-# Arbitrary text to print before XML (generally spaces to indent)
-prefix="$1"; shift
-
-# Allow building from a different directory
-cd "$(dirname $0)"
-
-list_candidates() {
- ls -1 "${1}.rng" "${1}"-[0-9]*.rng 2>/dev/null
-}
-
-version_from_filename() {
- vff_filename="$1"
-
- case "$vff_filename" in
- *-*.rng)
- echo "$vff_filename" | sed -e 's/.*-\(.*\).rng/\1/'
- ;;
- *)
- # special case for bare ${base}.rng, no -0.1's around anyway
- echo 0.1
- ;;
- esac
-}
-
-filename_from_version() {
- ffv_version="$1"
- ffv_base="$2"
-
- if [ "$ffv_version" = "0.1" ]; then
- echo "${ffv_base}.rng"
- else
- echo "${ffv_base}-${ffv_version}.rng"
- fi
-}
-
-# Convert version string (e.g. 2.10) into integer (e.g. 2010) for comparisons
-int_version() {
- echo "$1" | awk -F. '{ printf("%d%03d\n", $1,$2); }';
-}
-
-best="0.0"
-for rng in $(list_candidates "${base}"); do
- case ${rng} in
- ${base}-${target}.rng)
- # We found exactly what was requested
- best=${target}
- break
- ;;
- *-next.rng)
- # "Next" schemas cannot be a best match unless directly requested
- ;;
- *)
- v=$(version_from_filename "${rng}")
- if [ $(int_version "${v}") -gt $(int_version "${best}") ]; then
- # This version beats the previous best match
-
- if [ "${target}" = "next" ]; then
- best=${v}
- elif [ $(int_version "${v}") -lt $(int_version "${target}") ]; then
- # This value is best only if it's still less than the target
- best=${v}
- fi
- fi
- ;;
- esac
-done
-
-if [ "$best" != "0.0" ]; then
- found=$(filename_from_version "$best" "$base")
- if [ -z "$destination" ]; then
- echo "$(basename $found)"
- else
- echo "${prefix}<externalRef href=\"$(basename $found)\"/>" >> "$destination"
- fi
- exit 0
-fi
-
-exit 1
diff --git a/xml/constraints-3.10.rng b/xml/constraints-3.10.rng
new file mode 100644
index 0000000..5fbb7ac
--- /dev/null
+++ b/xml/constraints-3.10.rng
@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-constraints"/>
+ </start>
+
+ <define name="element-constraints">
+ <element name="constraints">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-location"/>
+ <ref name="element-colocation"/>
+ <ref name="element-order"/>
+ <ref name="element-rsc_ticket"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <define name="element-location">
+ <element name="rsc_location">
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <group>
+ <choice>
+ <attribute name="rsc"><data type="IDREF"/></attribute>
+ <attribute name="rsc-pattern"><text/></attribute>
+ </choice>
+ <optional>
+ <attribute name="role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ </group>
+ <oneOrMore>
+ <ref name="element-resource-set"/>
+ </oneOrMore>
+ </choice>
+ <choice>
+ <group>
+ <externalRef href="score.rng"/>
+ <attribute name="node"><text/></attribute>
+ </group>
+ <!--
+ @COMPAT: When we can break backward compatibility, limit this to a
+ single rule
+ -->
+ <oneOrMore>
+ <grammar>
+ <include href="rule-3.10.rng">
+ <start>
+ <ref name="element-rule-location"/>
+ </start>
+ </include>
+ </grammar>
+ </oneOrMore>
+ </choice>
+
+ <!-- @COMPAT: The lifetime element is deprecated -->
+ <optional>
+ <ref name="element-lifetime"/>
+ </optional>
+
+ <optional>
+ <attribute name="resource-discovery">
+ <ref name="attribute-discovery"/>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="element-resource-set">
+ <element name="resource_set">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="sequential"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="require-all"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="ordering">
+ <choice>
+ <value>group</value>
+ <value>listed</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="action">
+ <ref name="attribute-actions"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="kind">
+ <ref name="order-types"/>
+ </attribute>
+ </choice>
+ </optional>
+ <oneOrMore>
+ <element name="resource_ref">
+ <attribute name="id"><data type="IDREF"/></attribute>
+ </element>
+ </oneOrMore>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="element-colocation">
+ <element name="rsc_colocation">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <externalRef href="score.rng"/>
+ </optional>
+ <optional>
+ <attribute name="influence"><text/></attribute>
+ </optional>
+
+ <!-- @COMPAT: The lifetime element is deprecated -->
+ <optional>
+ <ref name="element-lifetime"/>
+ </optional>
+
+ <choice>
+ <oneOrMore>
+ <ref name="element-resource-set"/>
+ </oneOrMore>
+ <group>
+ <attribute name="rsc"><data type="IDREF"/></attribute>
+ <attribute name="with-rsc"><data type="IDREF"/></attribute>
+ <optional>
+ <attribute name="node-attribute"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="with-rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="element-order">
+ <element name="rsc_order">
+ <attribute name="id"><data type="ID"/></attribute>
+
+ <!-- @COMPAT: The lifetime element is deprecated -->
+ <optional>
+ <ref name="element-lifetime"/>
+ </optional>
+
+ <optional>
+ <attribute name="symmetrical"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="require-all"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="kind">
+ <ref name="order-types"/>
+ </attribute>
+ </choice>
+ </optional>
+ <choice>
+ <oneOrMore>
+ <ref name="element-resource-set"/>
+ </oneOrMore>
+ <group>
+ <attribute name="first"><data type="IDREF"/></attribute>
+ <attribute name="then"><data type="IDREF"/></attribute>
+ <optional>
+ <attribute name="first-action">
+ <ref name="attribute-actions"/>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="then-action">
+ <ref name="attribute-actions"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="element-rsc_ticket">
+ <element name="rsc_ticket">
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <oneOrMore>
+ <ref name="element-resource-set"/>
+ </oneOrMore>
+ <group>
+ <attribute name="rsc"><data type="IDREF"/></attribute>
+ <optional>
+ <attribute name="rsc-role">
+ <ref name="attribute-roles"/>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
+ <attribute name="ticket"><text/></attribute>
+ <optional>
+ <attribute name="loss-policy">
+ <choice>
+ <value>stop</value>
+ <value>demote</value>
+ <value>fence</value>
+ <value>freeze</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="attribute-discovery">
+ <choice>
+ <value>always</value>
+ <value>never</value>
+ <value>exclusive</value>
+ </choice>
+ </define>
+
+ <define name="attribute-actions">
+ <choice>
+ <value>start</value>
+ <value>promote</value>
+ <value>demote</value>
+ <value>stop</value>
+ </choice>
+ </define>
+
+ <define name="attribute-roles">
+ <choice>
+ <value>Stopped</value>
+ <value>Started</value>
+ <value>Promoted</value>
+ <value>Unpromoted</value>
+ <value>Master</value>
+ <value>Slave</value>
+ </choice>
+ </define>
+
+ <define name="order-types">
+ <choice>
+ <value>Optional</value>
+ <value>Mandatory</value>
+ <value>Serialize</value>
+ </choice>
+ </define>
+
+ <!-- @COMPAT: The lifetime element is deprecated -->
+ <define name="element-lifetime">
+ <element name="lifetime">
+ <oneOrMore>
+ <grammar>
+ <include href="rule-3.10.rng">
+ <start>
+ <ref name="element-rule-location"/>
+ </start>
+ </include>
+ </grammar>
+ </oneOrMore>
+ </element>
+ </define>
+
+</grammar>
diff --git a/xml/constraints-next.rng b/xml/constraints-next.rng
index 58fd27f..241b053 100644
--- a/xml/constraints-next.rng
+++ b/xml/constraints-next.rng
@@ -48,7 +48,7 @@
</group>
<oneOrMore>
<grammar>
- <include href="rule-3.9.rng">
+ <include href="rule-3.10.rng">
<start>
<ref name="element-rule-location"/>
</start>
@@ -276,7 +276,7 @@
<element name="lifetime">
<oneOrMore>
<grammar>
- <include href="rule-3.9.rng">
+ <include href="rule-3.10.rng">
<start>
<ref name="element-rule-location"/>
</start>
diff --git a/xml/nodes-3.10.rng b/xml/nodes-3.10.rng
new file mode 100644
index 0000000..ac2c557
--- /dev/null
+++ b/xml/nodes-3.10.rng
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-nodes"/>
+ </start>
+
+ <define name="element-nodes">
+ <element name="nodes">
+ <zeroOrMore>
+ <element name="node">
+ <attribute name="id"><text/></attribute>
+ <attribute name="uname"><text/></attribute>
+ <optional>
+ <attribute name="type">
+ <choice>
+ <value>member</value>
+ <value>remote</value>
+
+ <!-- @COMPAT ping nodes are deprecated since 2.1.3 -->
+ <value>ping</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <optional>
+ <externalRef href="score.rng"/>
+ </optional>
+ <zeroOrMore>
+ <choice>
+ <element name="instance_attributes">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ <element name="utilization">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+ </element>
+ </define>
+
+</grammar>
diff --git a/xml/nvset-3.10.rng b/xml/nvset-3.10.rng
new file mode 100644
index 0000000..07565d8
--- /dev/null
+++ b/xml/nvset-3.10.rng
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- just as nvset-2.9.rng, but allows for instantiated @name restrictions -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-nvset"/>
+ </start>
+
+ <!-- nvpair/@name:
+ * generic string by default, parent grammar may want to prohibit
+ enumerated names -->
+ <define name="element-nvset.name">
+ <attribute name="name">
+ <text/>
+ </attribute>
+ </define>
+
+ <!-- nvpair/@name:
+ * defer element-nvset.name grammar item
+ nvpair/@value:
+ generic string by default, parent grammar may want to restrict
+ enumerated pairs (i.e. related to @name) at once -->
+ <define name="element-nvset.name-value">
+ <ref name="element-nvset.name"/>
+ <optional>
+ <attribute name="value"><text/></attribute>
+ </optional>
+ </define>
+
+ <!-- Allow easy redefinition in parent grammars -->
+ <define name="element-nvset.rule">
+ <externalRef href="rule-3.10.rng"/>
+ </define>
+
+ <define name="element-nvset">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <attribute name="id"><data type="ID"/></attribute>
+ <interleave>
+ <optional>
+ <ref name="element-nvset.rule"/>
+ </optional>
+ <zeroOrMore>
+ <element name="nvpair">
+ <choice>
+ <group>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <optional>
+ <attribute name="name"><text/></attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="id"><data type="ID"/></attribute>
+ <ref name="element-nvset.name-value"/>
+ </group>
+ </choice>
+ </element>
+ </zeroOrMore>
+ <optional>
+ <externalRef href="score.rng"/>
+ </optional>
+ </interleave>
+ </group>
+ </choice>
+ </define>
+
+</grammar>
diff --git a/xml/options-3.10.rng b/xml/options-3.10.rng
new file mode 100644
index 0000000..68af956
--- /dev/null
+++ b/xml/options-3.10.rng
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="options"/>
+ </start>
+
+ <!--
+ Include rule definitions so that we can override element-nvset.rule based on
+ context
+ -->
+ <include href="rule-3.10.rng">
+ <start combine="choice">
+ <notAllowed/>
+ </start>
+ </include>
+
+ <!--
+ see upgrade-2.10.xsl
+ - cibtr:table for="cluster-properties"
+ -->
+ <define name="cluster_property_set.nvpair.name-value-unsupported">
+ <choice>
+ <group>
+ <attribute name="name">
+ <value type="string">cluster-infrastructure</value>
+ </attribute>
+ <attribute name="value">
+ <data type="string">
+ <except>
+ <choice>
+ <value>heartbeat</value>
+ <value>openais</value>
+ <value>classic openais</value>
+ <value>classic openais (with plugin)</value>
+ <value>cman</value>
+ </choice>
+ </except>
+ </data>
+ </attribute>
+ </group>
+ <group>
+ <attribute name="name">
+ <data type="string">
+ <except>
+ <choice>
+ <value>cluster-infrastructure</value>
+ <value>cluster_recheck_interval</value>
+ <value>dc_deadtime</value>
+ <value>default-action-timeout</value>
+ <value>default_action_timeout</value>
+ <value>default-migration-threshold</value>
+ <value>default_migration_threshold</value>
+ <value>default-resource-failure-stickiness</value>
+ <value>default_resource_failure_stickiness</value>
+ <value>default-resource-stickiness</value>
+ <value>default_resource_stickiness</value>
+ <value>election_timeout</value>
+ <value>expected-quorum-votes</value>
+ <value>is-managed-default</value>
+ <value>is_managed_default</value>
+ <value>no_quorum_policy</value>
+ <value>notification-agent</value>
+ <value>notification-recipient</value>
+ <value>remove_after_stop</value>
+ <value>shutdown_escalation</value>
+ <value>startup_fencing</value>
+ <value>stonith_action</value>
+ <value>stonith_enabled</value>
+ <value>stop_orphan_actions</value>
+ <value>stop_orphan_resources</value>
+ <value>symmetric_cluster</value>
+ <value>transition_idle_timeout</value>
+ </choice>
+ </except>
+ </data>
+ </attribute>
+ <optional>
+ <attribute name="value"><text/></attribute>
+ </optional>
+ </group>
+ </choice>
+ </define>
+
+ <define name="options">
+ <interleave>
+ <element name="crm_config">
+ <zeroOrMore>
+ <element name="cluster_property_set">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.name-value">
+ <parentRef name="cluster_property_set.nvpair.name-value-unsupported"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ </zeroOrMore>
+ </element>
+ <optional>
+ <element name="rsc_defaults">
+ <zeroOrMore>
+ <element name="meta_attributes">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.rule">
+ <parentRef name="element-rule-rsc_defaults"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="op_defaults">
+ <zeroOrMore>
+ <element name="meta_attributes">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.rule">
+ <parentRef name="element-rule-op_defaults"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </interleave>
+ </define>
+
+</grammar>
diff --git a/xml/resources-3.10.rng b/xml/resources-3.10.rng
new file mode 100644
index 0000000..e540617
--- /dev/null
+++ b/xml/resources-3.10.rng
@@ -0,0 +1,451 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-resources"/>
+ </start>
+
+ <!--
+ Include rule definitions so that we can override element-nvset.rule based on
+ context
+ -->
+ <include href="rule-3.10.rng">
+ <start combine="choice">
+ <notAllowed/>
+ </start>
+ </include>
+
+ <define name="element-resources">
+ <element name="resources">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-primitive"/>
+ <ref name="element-template"/>
+ <ref name="element-group"/>
+ <ref name="element-clone"/>
+ <ref name="element-master"/>
+ <ref name="element-bundle"/>
+ </choice>
+ </zeroOrMore>
+ </element>
+ </define>
+
+ <!--
+ see upgrade-2.10.xsl
+ - cibtr:table for="resource-meta-attributes"
+ -->
+ <define name="primitive-template.meta_attributes.nvpair.name-unsupported">
+ <attribute name="name">
+ <data type="string">
+ <except>
+ <choice>
+ <value>isolation</value>
+ <value>isolation-host</value>
+ <value>isolation-instance</value>
+ <value>isolation-wrapper</value>
+ </choice>
+ </except>
+ </data>
+ </attribute>
+ </define>
+
+ <define name="element-resource-extra.primitive-template">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-instance_attributes"/>
+ <element name="meta_attributes">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.name">
+ <parentRef name="primitive-template.meta_attributes.nvpair.name-unsupported"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ <element name="utilization">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+ <define name="element-primitive">
+ <element name="primitive">
+ <interleave>
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <group>
+ <ref name="element-resource-class"/>
+ <attribute name="type"><text/></attribute>
+ </group>
+ <attribute name="template"><data type="IDREF"/></attribute>
+ </choice>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <ref name="element-resource-extra.primitive-template"/>
+ <ref name="element-operations"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-template">
+ <element name="template">
+ <interleave>
+ <attribute name="id"><data type="ID"/></attribute>
+ <ref name="element-resource-class"/>
+ <attribute name="type"><text/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <ref name="element-resource-extra.primitive-template"/>
+ <ref name="element-operations"/>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-bundle">
+ <element name="bundle">
+ <interleave>
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <ref name="element-resource-extra"/>
+ <choice>
+ <element name="docker">
+ <attribute name="image"><text/></attribute>
+ <optional>
+ <attribute name="replicas"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="replicas-per-host"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <attribute name="masters"><data type="integer"/></attribute>
+ <attribute name="promoted-max"><data type="integer"/></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="run-command"> <text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="network"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="options"><text/></attribute>
+ </optional>
+ </element>
+
+ <!-- @COMPAT rkt containers in bundles are deprecated since 2.1.8 -->
+ <element name="rkt">
+ <attribute name="image"><text/></attribute>
+ <optional>
+ <attribute name="replicas"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="replicas-per-host"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <attribute name="masters"><data type="integer"/></attribute>
+ <attribute name="promoted-max"><data type="integer"/></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="run-command"> <text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="network"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="options"><text/></attribute>
+ </optional>
+ </element>
+ <element name="podman">
+ <attribute name="image"><text/></attribute>
+ <optional>
+ <attribute name="replicas"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="replicas-per-host"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <attribute name="masters"><data type="integer"/></attribute>
+ <attribute name="promoted-max"><data type="integer"/></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="run-command"> <text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="network"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="options"><text/></attribute>
+ </optional>
+ </element>
+ </choice>
+ <optional>
+ <element name="network">
+ <optional>
+ <attribute name="ip-range-start"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="control-port"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="host-interface"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="host-netmask"><data type="integer"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="add-host"><data type="boolean"/></attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="port-mapping">
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <group>
+ <attribute name="port"><data type="integer"/></attribute>
+ <optional>
+ <attribute name="internal-port"><data type="integer"/></attribute>
+ </optional>
+ </group>
+ <attribute name="range">
+ <data type="string">
+ <param name="pattern">([0-9\-]+)</param>
+ </data>
+ </attribute>
+ </choice>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <element name="storage">
+ <zeroOrMore>
+ <element name="storage-mapping">
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <attribute name="source-dir"><text/></attribute>
+ <attribute name="source-dir-root"><text/></attribute>
+ </choice>
+ <attribute name="target-dir"><text/></attribute>
+ <optional>
+ <attribute name="options"><text/></attribute>
+ </optional>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <optional>
+ <ref name="element-primitive"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-group">
+ <element name="group">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <interleave>
+ <ref name="element-resource-extra"/>
+ <oneOrMore>
+ <ref name="element-primitive"/>
+ </oneOrMore>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-clone">
+ <element name="clone">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <interleave>
+ <ref name="element-resource-extra"/>
+ <choice>
+ <ref name="element-primitive"/>
+ <ref name="element-group"/>
+ </choice>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-master">
+ <element name="master">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <interleave>
+ <ref name="element-resource-extra"/>
+ <choice>
+ <ref name="element-primitive"/>
+ <ref name="element-group"/>
+ </choice>
+ </interleave>
+ </element>
+ </define>
+
+ <define name="element-resource-extra">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-instance_attributes"/>
+ <element name="meta_attributes">
+ <externalRef href="nvset-3.10.rng"/>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+ <!--
+ see upgrade-2.10.xsl
+ - cibtr:table for="resources-operation"
+ -->
+ <define name="op.meta_attributes.nvpair.name-unsupported">
+ <attribute name="name">
+ <data type="string">
+ <except>
+ <choice>
+ <value>requires</value>
+ </choice>
+ </except>
+ </data>
+ </attribute>
+ </define>
+
+ <define name="element-resource-extra.op">
+ <zeroOrMore>
+ <choice>
+ <ref name="element-instance_attributes"/>
+ <element name="meta_attributes">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.name">
+ <parentRef name="op.meta_attributes.nvpair.name-unsupported"/>
+ </define>
+
+ <!--
+ @COMPAT: Support for node attribute expressions is deprecated
+ here. We can just delete this define when we drop support.
+ -->
+ <define name="element-nvset.rule">
+ <parentRef name="element-rule-node-allowed"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ </choice>
+ </zeroOrMore>
+ </define>
+
+ <define name="element-operations">
+ <optional>
+ <element name="operations">
+ <optional>
+ <attribute name="id"><data type="ID"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ </optional>
+ <zeroOrMore>
+ <element name="op">
+ <attribute name="id"><data type="ID"/></attribute>
+ <attribute name="name"><text/></attribute>
+ <attribute name="interval"><text/></attribute>
+ <optional>
+ <attribute name="description"><text/></attribute>
+ </optional>
+ <optional>
+ <choice>
+ <attribute name="start-delay"><text/></attribute>
+ <attribute name="interval-origin"><text/></attribute>
+ </choice>
+ </optional>
+ <optional>
+ <attribute name="timeout"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="enabled"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="record-pending"><data type="boolean"/></attribute>
+ </optional>
+ <optional>
+ <attribute name="role">
+ <choice>
+ <value>Stopped</value>
+ <value>Started</value>
+ <value>Promoted</value>
+ <value>Unpromoted</value>
+ <value>Slave</value>
+ <value>Master</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="on-fail">
+ <choice>
+ <value>ignore</value>
+ <value>block</value>
+ <value>demote</value>
+ <value>stop</value>
+ <value>restart</value>
+ <value>standby</value>
+ <value>fence</value>
+ <value>restart-container</value>
+ </choice>
+ </attribute>
+ </optional>
+ <ref name="element-resource-extra.op"/>
+ </element>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </define>
+
+ <define name="element-resource-class">
+ <choice>
+ <group>
+ <attribute name="class"><value>ocf</value></attribute>
+ <attribute name="provider"><text/></attribute>
+ </group>
+ <attribute name="class">
+ <choice>
+ <value>lsb</value>
+ <value>heartbeat</value>
+ <value>stonith</value>
+ <value>service</value>
+ <value>systemd</value>
+
+ <!-- @COMPAT upstart resources are deprecated since 2.1.0 -->
+ <value>upstart</value>
+
+ <!-- @COMPAT nagios resources are deprecated since 2.1.6 -->
+ <value>nagios</value>
+ </choice>
+ </attribute>
+ </choice>
+ </define>
+
+ <define name="element-instance_attributes">
+ <element name="instance_attributes">
+ <grammar>
+ <include href="nvset-3.10.rng">
+ <define name="element-nvset.rule">
+ <parentRef name="element-rule-node-allowed"/>
+ </define>
+ </include>
+ </grammar>
+ </element>
+ </define>
+</grammar>
diff --git a/xml/rng-helper.in b/xml/rng-helper.in
new file mode 100755
index 0000000..634abdf
--- /dev/null
+++ b/xml/rng-helper.in
@@ -0,0 +1,251 @@
+#!@BASH_PATH@
+#
+# Copyright 2014-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.
+#
+
+# @COMPAT "next" schemas are deprecated since 2.1.5
+list_candidates() {
+ ls -1 "${1}.rng" "${1}"-[0-9]*.rng "${1}"-next.rng 2>/dev/null
+}
+
+version_from_filename() {
+ vff_filename="$1"
+
+ case "$vff_filename" in
+ *-*.rng)
+ echo "$vff_filename" | sed -e 's/.*-\(.*\).rng/\1/'
+ ;;
+ *)
+ # special case for bare ${base}.rng, no -0.1's around anyway
+ echo 0.1
+ ;;
+ esac
+}
+
+filename_from_version() {
+ ffv_version="$1"
+ ffv_base="$2"
+
+ if [ "$ffv_version" = "0.1" ]; then
+ echo "${ffv_base}.rng"
+ else
+ echo "${ffv_base}-${ffv_version}.rng"
+ fi
+}
+
+# Convert version string (e.g. 2.10) into integer (e.g. 2010) for comparisons
+int_version() {
+ echo "$1" | awk -F. '{ printf("%d%03d\n", $1,$2); }';
+}
+
+# Find the (sub-)schema that best matches a desired version.
+#
+# Version numbers are assumed to be in the format X.Y,
+# where X and Y are integers, and Y is no more than 3 digits,
+# or the special value "next".
+#
+# @COMPAT "next" schemas are deprecated since 2.1.5
+best_match() {
+ # (Sub-)schema name (e.g. "resources")
+ local base="$1"
+
+ # Desired version (e.g. "1.0" or "next")
+ local target="$2"
+
+ # If not empty, append the best match as an XML externalRef to this file
+ # (otherwise, just echo the best match).
+ local destination="$3"
+
+ # Arbitrary text to print before XML (generally spaces to indent)
+ local prefix="$4"
+
+ best="0.0"
+ for rng in $(list_candidates "${base}"); do
+ case ${rng} in
+ ${base}-${target}.rng)
+ # We found exactly what was requested
+ best=${target}
+ break
+ ;;
+ *-next.rng)
+ # "Next" schemas cannot be a best match unless directly requested
+ ;;
+ *)
+ v=$(version_from_filename "${rng}")
+ if [ $(int_version "${v}") -gt $(int_version "${best}") ]; then
+ # This version beats the previous best match
+
+ if [ "${target}" = "next" ]; then
+ best=${v}
+ elif [ $(int_version "${v}") -lt $(int_version "${target}") ]; then
+ # This value is best only if it's still less than the target
+ best=${v}
+ fi
+ fi
+ ;;
+ esac
+ done
+
+ if [ "$best" != "0.0" ]; then
+ found=$(filename_from_version "$best" "$base")
+ if [ -z "$destination" ]; then
+ echo "$(basename $found)"
+ else
+ echo "${prefix}<externalRef href=\"$(basename $found)\"/>" >> "$destination"
+ fi
+ return 0
+ fi
+ return 1
+}
+
+version_diff() {
+ # diff fails with ec=2 if no predecessor is found;
+ # this uses '=' GNU extension to sed, if that's not available,
+ # one can use: hline=`echo "$${p}" | grep -Fn "$${hunk}" | cut -d: -f1`;
+ # XXX: use line information from hunk to avoid "not detected" for ambiguity
+ for p in $*; do
+ set $(echo "$p" | tr '-' ' ')
+ echo "### *-$2.rng vs. predecessor"
+
+ for v in *-"$2".rng; do
+ echo "#### $v vs. predecessor"
+
+ b=$(echo "$v" | cut -d- -f1)
+ old=$(best_match "$b" "$1")
+ p=$(diff -u "$old" "$v" 2>/dev/null)
+
+ case $? in
+ 1)
+ echo "$p" | sed -n -e '/^@@ /!d;=;p' -e ':l;n;/^\([- ]\|+.*<[^ />]\+\([^/>]\+="ID\|>$$\)\)/bl;s/^[+ ]\(.*\)/\1/p' |
+ while read -r hline; do
+ if read -r h; then
+ read -r i
+ else
+ break
+ fi
+
+ iline=$(grep -Fn "$i" "$v" | cut -d: -f1)
+
+ if [ "$(echo "$iline" | wc -l)" = "1" ]; then
+ ctxt=$({ sed -n -e "1,$((iline - 1))p" "$v"
+ echo "<inject id=\"GOAL\"/>$i"
+ sed -n -e "$((iline + 1)),$ p" "$v"
+ } | xsltproc --param skip 1 context-of.xsl -)
+ else
+ ctxt="(not detected)"
+ fi
+
+ echo "$p" | sed -n -e "$((hline - 2)),$hline!d" -e '/^\(+++\|---\)/p'
+ echo "$h context: $ctxt"
+ echo "$p" | sed -n -e "1,${hline}d" -e '/^\(---\|@@ \)/be;p;d;:e;n;be'
+ done
+
+ ;;
+
+ 2)
+ echo "##### $v has no predecessor"
+ ;;
+
+ esac
+ done
+ done
+}
+
+build_api_rng() {
+ local FILENAME="$1"
+ local VERSION="$2"
+
+ shift 2
+
+ cat <<EOF >"$FILENAME"
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <element name="pacemaker-result">
+ <attribute name="api-version"> <text /> </attribute>
+ <attribute name="request"> <text /> </attribute>
+ <optional>
+ <choice>
+EOF
+ for RNG in "$@"; do
+ best_match "api/$RNG" "$VERSION" "$FILENAME" " "
+ done
+ cat <<EOF >>"$FILENAME"
+ </choice>
+ </optional>
+EOF
+ best_match api/status "$VERSION" "$FILENAME" " "
+ cat <<EOF >>"$FILENAME"
+ </element>
+ </start>
+</grammar>
+EOF
+}
+
+build_cib_rng() {
+ local FILENAME="$1"
+ local VERSION="$2"
+
+ shift 2
+ cat <<EOF >"$FILENAME"
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <element name="cib">
+EOF
+ best_match cib "$VERSION" "$FILENAME" " "
+ cat <<EOF >>"$FILENAME"
+ <element name="configuration">
+ <interleave>
+EOF
+ for RNG in "$@"; do
+ best_match "$RNG" "$VERSION" "$FILENAME" " "
+ done
+ cat <<EOF >>"$FILENAME"
+ </interleave>
+ </element>
+ <optional>
+ <element name="status">
+EOF
+ best_match status "$VERSION" "$FILENAME" " "
+ cat <<EOF >>"$FILENAME"
+ </element>
+ </optional>
+ </element>
+ </start>
+</grammar>
+EOF
+}
+
+# Allow building RNGs from a different directory
+cd "$(dirname $0)"
+
+case "$1" in
+ match)
+ # Using readlink allows building from a different directory
+ best_match "$2" "$3" "$(readlink -f "$4")" "$5"
+ ;;
+
+ diff)
+ shift
+ version_diff "$@"
+ ;;
+
+ build_api_rng)
+ build_api_rng "$2" "$3" "${@:4}"
+ ;;
+
+ build_cib_rng)
+ build_cib_rng "$2" "$3" "${@:4}"
+ ;;
+
+ *)
+ echo "Invalid command: $1"
+ exit 1
+ ;;
+esac
diff --git a/xml/rule-3.10.rng b/xml/rule-3.10.rng
new file mode 100644
index 0000000..6ef362f
--- /dev/null
+++ b/xml/rule-3.10.rng
@@ -0,0 +1,433 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ xmlns:ann="http://relaxng.org/ns/compatibility/annotations/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+ <start>
+ <ref name="element-rule"/>
+ </start>
+
+ <!--
+ Rule elements allow different syntax depending on their context, each of
+ which gets their own rule definition below:
+
+ 1. rsc_location
+ 2. meta_attributes within rsc_defaults
+ 3. meta_attributes within op_defaults
+ 4. all other contexts that allow node attribute expressions
+ (instance_attributes elements within bundle, clone, group, op, primitive,
+ and template elements; and meta_attributes elements within op elements)
+ 5. all other contexts (cluster_property_set elements; instance_attributes
+ within alert, node, and recipient elements; meta_attributes within alert,
+ bundle, clone, group, primitive, recipient, and template elements; and
+ utilization elements within node, primitive, and template elements)
+
+ The COMPAT comments below mark items that are invalid in their given context
+ and should be removed at a compatibility break.
+ -->
+
+ <!-- 1. rule element within rsc_location -->
+ <define name="element-rule-location">
+ <element name="rule">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <ref name="rule-common"/>
+ <oneOrMore>
+ <choice>
+ <ref name="expression-location"/>
+ <ref name="date_expression"/>
+ <ref name="element-rule-location"/>
+
+ <!-- @COMPAT: The below expression types are invalid here -->
+ <ref name="rsc_expression"/>
+ <ref name="op_expression"/>
+ </choice>
+ </oneOrMore>
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="score-attribute"><text/></attribute>
+ </choice>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <!-- 2. rule element within rsc_defaults -->
+ <define name="element-rule-rsc_defaults">
+ <element name="rule">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <ref name="rule-common"/>
+ <oneOrMore>
+ <choice>
+ <ref name="date_expression"/>
+ <ref name="rsc_expression"/>
+ <ref name="element-rule-rsc_defaults"/>
+
+ <!-- @COMPAT: The below expression type is deprecated here -->
+ <ref name="expression"/>
+
+ <!-- @COMPAT: The below expression type is invalid here -->
+ <ref name="op_expression"/>
+ </choice>
+ </oneOrMore>
+
+ <!-- @COMPAT: The below score attributes are invalid here -->
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="score-attribute"><text/></attribute>
+ </choice>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <!-- 3. rule element within op_defaults -->
+ <define name="element-rule-op_defaults">
+ <element name="rule">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <ref name="rule-common"/>
+ <oneOrMore>
+ <choice>
+ <ref name="date_expression"/>
+ <ref name="rsc_expression"/>
+ <ref name="op_expression"/>
+ <ref name="element-rule-op_defaults"/>
+
+ <!-- @COMPAT: The below expression type is deprecated here -->
+ <ref name="expression"/>
+ </choice>
+ </oneOrMore>
+
+ <!-- @COMPAT: The below score attributes are invalid here -->
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="score-attribute"><text/></attribute>
+ </choice>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+
+ <!-- 4. rule element in other contexts allowing node attribute expressions -->
+ <define name="element-rule-node-allowed">
+ <element name="rule">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <ref name="rule-common"/>
+ <oneOrMore>
+ <choice>
+ <ref name="expression"/>
+ <ref name="date_expression"/>
+ <ref name="element-rule-node-allowed"/>
+
+ <!-- @COMPAT: The below expression types are invalid here -->
+ <ref name="rsc_expression"/>
+ <ref name="op_expression"/>
+ </choice>
+ </oneOrMore>
+
+ <!-- @COMPAT: The below score attributes are invalid here -->
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="score-attribute"><text/></attribute>
+ </choice>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <!-- 5. rule element within all other contexts -->
+ <define name="element-rule">
+ <element name="rule">
+ <choice>
+ <attribute name="id-ref"><data type="IDREF"/></attribute>
+ <group>
+ <ref name="rule-common"/>
+ <oneOrMore>
+ <choice>
+ <ref name="date_expression"/>
+ <ref name="element-rule"/>
+
+ <!-- @COMPAT: The below expression types are invalid here -->
+ <ref name="expression"/>
+ <ref name="rsc_expression"/>
+ <ref name="op_expression"/>
+ </choice>
+ </oneOrMore>
+
+ <!-- @COMPAT: The below score attributes are invalid here -->
+ <optional>
+ <choice>
+ <externalRef href="score.rng"/>
+ <attribute name="score-attribute"><text/></attribute>
+ </choice>
+ </optional>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <!-- Attributes that are common to all rule elements -->
+ <define name="rule-common">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="boolean-op">
+ <choice>
+ <value>or</value>
+ <value>and</value>
+ </choice>
+ </attribute>
+ </optional>
+
+ <!--
+ @COMPAT Role applies only to rules within location constraints. When we can
+ break behavioral backward compatibility, move this to
+ rule-element-location.
+ -->
+ <optional>
+ <attribute name="role"><text/></attribute>
+ </optional>
+ </define>
+
+ <!-- A node attribute expression -->
+ <define name="expression">
+ <element name="expression">
+ <ref name="expression-common"/>
+
+ <optional>
+ <attribute name="value-source" ann:defaultValue="literal">
+ <choice>
+ <value>literal</value>
+ <!-- @COMPAT: These value-source choices are invalid here -->
+ <value>param</value>
+ <value>meta</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <!-- A node attribute expression in a location constraint -->
+ <define name="expression-location">
+ <element name="expression">
+ <ref name="expression-common"/>
+ <optional>
+ <attribute name="value-source" ann:defaultValue="literal">
+ <choice>
+ <value>literal</value>
+ <value>param</value>
+ <value>meta</value>
+ </choice>
+ </attribute>
+ </optional>
+ </element>
+ </define>
+
+ <!-- Attributes that are common to all <expression> elements -->
+ <define name="expression-common">
+ <attribute name="id"><data type="ID"/></attribute>
+ <attribute name="attribute"><text/></attribute>
+ <choice>
+ <group>
+ <attribute name="operation">
+ <choice>
+ <value>defined</value>
+ <value>not_defined</value>
+ </choice>
+ </attribute>
+ <!-- @COMPAT value attribute should be prohibited here -->
+ <optional>
+ <attribute name="value"><text/></attribute>
+ </optional>
+ </group>
+ <group>
+ <attribute name="operation">
+ <choice>
+ <value>lt</value>
+ <value>gt</value>
+ <value>lte</value>
+ <value>gte</value>
+ <value>eq</value>
+ <value>ne</value>
+ </choice>
+ </attribute>
+ <!-- @COMPAT value attribute should be required here -->
+ <optional>
+ <attribute name="value"><text/></attribute>
+ </optional>
+ </group>
+ </choice>
+ <optional>
+ <attribute name="type" ann:defaultValue="string">
+ <choice>
+ <value>string</value>
+ <value>integer</value>
+ <value>number</value>
+ <value>version</value>
+ </choice>
+ </attribute>
+ </optional>
+ </define>
+
+ <define name="date_expression">
+ <element name="date_expression">
+ <attribute name="id"><data type="ID"/></attribute>
+ <choice>
+ <group>
+ <attribute name="operation"><value>in_range</value></attribute>
+ <choice>
+ <attribute name="start"><text/></attribute>
+ <attribute name="end"><text/></attribute>
+ <group>
+ <attribute name="start"><text/></attribute>
+ <attribute name="end"><text/></attribute>
+ </group>
+ </choice>
+ <optional>
+ <ref name="duration"/>
+ </optional>
+ </group>
+ <group>
+ <attribute name="operation"><value>gt</value></attribute>
+ <attribute name="start"><text/></attribute>
+ </group>
+ <group>
+ <attribute name="operation"><value>lt</value></attribute>
+ <attribute name="end"><text/></attribute>
+ </group>
+ <group>
+ <attribute name="operation"><value>date_spec</value></attribute>
+ <ref name="date_spec"/>
+ </group>
+ </choice>
+ </element>
+ </define>
+
+ <define name="rsc_expression">
+ <element name="rsc_expression">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="class"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="provider"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="type"><text/></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="op_expression">
+ <element name="op_expression">
+ <attribute name="id"><data type="ID"/></attribute>
+ <attribute name="name"><text/></attribute>
+ <optional>
+ <attribute name="interval"><text/></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="duration">
+ <element name="duration">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="years"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="months"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weeks"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="days"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="hours"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="minutes"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="seconds"><text/></attribute>
+ </optional>
+
+ <!-- @COMPAT: The below attributes are invalid here -->
+ <optional>
+ <attribute name="monthdays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weekdays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="yearsdays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weekyears"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="moon"><text/></attribute>
+ </optional>
+ </element>
+ </define>
+
+ <define name="date_spec">
+ <element name="date_spec">
+ <attribute name="id"><data type="ID"/></attribute>
+ <optional>
+ <attribute name="years"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="months"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="monthdays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="hours"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="minutes"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="seconds"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="yeardays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weekyears"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weeks"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="weekdays"><text/></attribute>
+ </optional>
+ <optional>
+ <attribute name="moon"><text/></attribute>
+ </optional>
+
+ <!-- @COMPAT: The below attributes are invalid here -->
+ <optional>
+ <attribute name="yearsdays"><text/></attribute>
+ </optional>
+ </element>
+ </define>
+
+</grammar>
diff --git a/xml/version-diff.sh.in b/xml/version-diff.sh.in
deleted file mode 100644
index 1ece3b3..0000000
--- a/xml/version-diff.sh.in
+++ /dev/null
@@ -1,60 +0,0 @@
-#!@BASH_PATH@
-#
-# Copyright 2016-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.
-#
-
-# diff fails with ec=2 if no predecessor is found;
-# this uses '=' GNU extension to sed, if that's not available,
-# one can use: hline=`echo "$${p}" | grep -Fn "$${hunk}" | cut -d: -f1`;
-# XXX: use line information from hunk to avoid "not detected" for ambiguity
-for p in $*; do
- set $(echo "$p" | tr '-' ' ')
- echo "### *-$2.rng vs. predecessor"
-
- for v in *-"$2".rng; do
- echo "#### $v vs. predecessor"
-
- b=$(echo "$v" | cut -d- -f1)
- old=$(./best-match.sh "$b" "$1")
- p=$(diff -u "$old" "$v" 2>/dev/null)
-
- case $? in
- 1)
- echo "$p" | sed -n -e '/^@@ /!d;=;p' -e ':l;n;/^\([- ]\|+.*<[^ />]\+\([^/>]\+="ID\|>$$\)\)/bl;s/^[+ ]\(.*\)/\1/p' |
- while read -r hline; do
- if read -r h; then
- read -r i
- else
- break
- fi
-
- iline=$(grep -Fn "$i" "$v" | cut -d: -f1)
-
- if [ "$(echo "$iline" | wc -l)" = "1" ]; then
- ctxt=$({ sed -n -e "1,$((iline - 1))p" "$v"
- echo "<inject id=\"GOAL\"/>$i"
- sed -n -e "$((iline + 1)),$ p" "$v"
- } | xsltproc --param skip 1 context-of.xsl -)
- else
- ctxt="(not detected)"
- fi
-
- echo "$p" | sed -n -e "$((hline - 2)),$hline!d" -e '/^\(+++\|---\)/p'
- echo "$h context: $ctxt"
- echo "$p" | sed -n -e "1,${hline}d" -e '/^\(---\|@@ \)/be;p;d;:e;n;be'
- done
-
- ;;
-
- 2)
- echo "##### $v has no predecessor"
- ;;
-
- esac
- done
-done