diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
index 9b5f400e4b..7a79f3a8a9 100644
--- a/cts/cli/regression.tools.exp
+++ b/cts/cli/regression.tools.exp
@@ -1,9274 +1,9274 @@
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: Validate CIB =#=#=#=
=#=#=#= Current cib after: Validate CIB =#=#=#=
=#=#=#= End test: Validate CIB - OK (0) =#=#=#=
* Passed: cibadmin - Validate CIB
=#=#=#= 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): string (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: time (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: time (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: time (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: time (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: time (default: )
* stonith-max-attempts: How many times fencing can fail before it will no longer be immediately re-attempted on a target
* Possible values: integer (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: time (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: time (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: time (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: 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: integer (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: integer (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: integer (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: integer (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) =#=#=#=
1.1Also 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.Pacemaker cluster optionsIncludes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.Pacemaker version on cluster node elected Designated Controller (DC)Used for informational and diagnostic purposes.The messaging layer on which Pacemaker is currently runningThis 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.An arbitrary name for the clusterThe optimal value will depend on the speed and load of your network and the type of switches used.How long to wait for a response from other nodes during start-upPacemaker 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").Polling interval to recheck cluster state and evaluate rules with date specificationsA 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.How a cluster node should react if notified of its own fencingDeclare 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.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.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.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.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.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.Enabling this option will slow down cluster recovery under all conditionsWhat to do when the cluster does not have quorumWhat to do when the cluster does not have quorumWhen 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.Whether to lock resources to a cleanly shut down nodeIf 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.Do not lock resources to a cleanly shut down node longer than thisEnable Access Control Lists (ACLs) for the CIBEnable Access Control Lists (ACLs) for the CIBWhether resources can run on any node by defaultWhether resources can run on any node by defaultWhether the cluster should refrain from monitoring, starting, and stopping resourcesWhether the cluster should refrain from monitoring, starting, and stopping resourcesWhen 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.Whether a start failure should prevent a resource from being recovered on the same nodeWhether the cluster should check for active resources during start-upWhether the cluster should check for active resources during start-upIf 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.Whether nodes may be fenced as part of recoveryAction to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")How long to wait for on, off, and reboot fence actions to complete by defaultHow long to wait for on, off, and reboot fence actions to complete by defaultThis 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.Whether watchdog integration is enabledIf 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.How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in useHow many times fencing can fail before it will no longer be immediately re-attempted on a targetHow many times fencing can fail before it will no longer be immediately re-attempted on a targetAllow performing fencing operations in parallelAllow performing fencing operations in parallelSetting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.Whether to fence unseen nodes at start-upApply 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.Apply fencing delay targeting the lost nodes with the highest total resource priorityFence 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.How long to wait for a node that has joined the cluster to join the controller process groupThe 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.Maximum time for node-to-node communicationThe cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limitMaximum amount of system load that should be used by cluster nodesMaximum number of jobs that can be scheduled per node (defaults to 2x cores)Maximum number of jobs that can be scheduled per node (defaults to 2x cores)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.Maximum number of jobs that the cluster may execute in parallel across all nodesThe number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)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).Maximum IPC message backlog before disconnecting a cluster daemonWhether the cluster should stop all active resourcesWhether the cluster should stop all active resourcesWhether to stop resources that were removed from the configurationWhether to stop resources that were removed from the configurationWhether to cancel recurring actions removed from the configurationWhether to cancel recurring actions removed from the configurationValues other than default are poorly tested and potentially dangerous.Whether to remove stopped resources from the executorZero to disable, -1 to store unlimited.The number of scheduler inputs resulting in errors to saveZero to disable, -1 to store unlimited.The number of scheduler inputs resulting in warnings to saveZero to disable, -1 to store unlimited.The number of scheduler inputs without errors or warnings to saveRequires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".How cluster should react to node health attributesOnly used when "node-health-strategy" is set to "progressive".Base health score assigned to a nodeOnly used when "node-health-strategy" is set to "custom" or "progressive".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".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".The score to use for a node health attribute whose value is "red"How the cluster should allocate resources to nodesHow the cluster should allocate resources to nodes
=#=#=#= End test: List non-advanced cluster options (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - List non-advanced cluster options (XML)
=#=#=#= 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): string (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: time (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: time (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: time (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: time (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: time (default: )
* stonith-max-attempts: How many times fencing can fail before it will no longer be immediately re-attempted on a target
* Possible values: integer (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: time (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: time (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: time (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: 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: integer (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: integer (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: integer (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: integer (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: time (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: time (default: )
* join-integration-timeout: If you need to adjust this value, it probably indicates the presence of a bug.
* Possible values: time (default: )
* join-finalization-timeout: If you need to adjust this value, it probably indicates the presence of a bug.
* Possible values: time (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: time (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) =#=#=#=
1.1Also 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.Pacemaker cluster optionsIncludes a hash which identifies the exact revision the code was built from. Used for diagnostic purposes.Pacemaker version on cluster node elected Designated Controller (DC)Used for informational and diagnostic purposes.The messaging layer on which Pacemaker is currently runningThis 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.An arbitrary name for the clusterThe optimal value will depend on the speed and load of your network and the type of switches used.How long to wait for a response from other nodes during start-upPacemaker 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").Polling interval to recheck cluster state and evaluate rules with date specificationsA 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.How a cluster node should react if notified of its own fencingDeclare 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.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.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.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.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.If you need to adjust this value, it probably indicates the presence of a bug.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.Enabling this option will slow down cluster recovery under all conditionsWhat to do when the cluster does not have quorumWhat to do when the cluster does not have quorumWhen 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.Whether to lock resources to a cleanly shut down nodeIf 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.Do not lock resources to a cleanly shut down node longer than thisEnable Access Control Lists (ACLs) for the CIBEnable Access Control Lists (ACLs) for the CIBWhether resources can run on any node by defaultWhether resources can run on any node by defaultWhether the cluster should refrain from monitoring, starting, and stopping resourcesWhether the cluster should refrain from monitoring, starting, and stopping resourcesWhen 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.Whether a start failure should prevent a resource from being recovered on the same nodeWhether the cluster should check for active resources during start-upWhether the cluster should check for active resources during start-upIf 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.Whether nodes may be fenced as part of recoveryAction to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")Action to send to fence device when a node needs to be fenced ("poweroff" is a deprecated alias for "off")How long to wait for on, off, and reboot fence actions to complete by defaultHow long to wait for on, off, and reboot fence actions to complete by defaultThis 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.Whether watchdog integration is enabledIf 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.How long before nodes can be assumed to be safely down when watchdog-based self-fencing via SBD is in useHow many times fencing can fail before it will no longer be immediately re-attempted on a targetHow many times fencing can fail before it will no longer be immediately re-attempted on a targetAllow performing fencing operations in parallelAllow performing fencing operations in parallelSetting this to false may lead to a "split-brain" situation, potentially leading to data loss and/or service unavailability.Whether to fence unseen nodes at start-upApply 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.Apply fencing delay targeting the lost nodes with the highest total resource priorityFence 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.How long to wait for a node that has joined the cluster to join the controller process groupThe 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.Maximum time for node-to-node communicationThe cluster will slow down its recovery process when the amount of system resources used (currently CPU) approaches this limitMaximum amount of system load that should be used by cluster nodesMaximum number of jobs that can be scheduled per node (defaults to 2x cores)Maximum number of jobs that can be scheduled per node (defaults to 2x cores)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.Maximum number of jobs that the cluster may execute in parallel across all nodesThe number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)The number of live migration actions that the cluster is allowed to execute in parallel on a node (-1 means no limit)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).Maximum IPC message backlog before disconnecting a cluster daemonWhether the cluster should stop all active resourcesWhether the cluster should stop all active resourcesWhether to stop resources that were removed from the configurationWhether to stop resources that were removed from the configurationWhether to cancel recurring actions removed from the configurationWhether to cancel recurring actions removed from the configurationValues other than default are poorly tested and potentially dangerous.Whether to remove stopped resources from the executorZero to disable, -1 to store unlimited.The number of scheduler inputs resulting in errors to saveZero to disable, -1 to store unlimited.The number of scheduler inputs resulting in warnings to saveZero to disable, -1 to store unlimited.The number of scheduler inputs without errors or warnings to saveRequires external entities to create node attributes (named with the prefix "#health") with values "red", "yellow", or "green".How cluster should react to node health attributesOnly used when "node-health-strategy" is set to "progressive".Base health score assigned to a nodeOnly used when "node-health-strategy" is set to "custom" or "progressive".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".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".The score to use for a node health attribute whose value is "red"How the cluster should allocate resources to nodesHow the cluster should allocate resources to nodes
=#=#=#= End test: List all available cluster options (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - List all available cluster options (XML)
=#=#=#= Begin test: List all available options (invalid value) =#=#=#=
crm_attribute: Invalid --list-options value 'asdf'. Allowed values: cluster
=#=#=#= End test: List all available options (invalid value) - Incorrect usage (64) =#=#=#=
* Passed: crm_attribute - List all available options (invalid value)
=#=#=#= Begin test: List all available cluster options (invalid value) (XML) =#=#=#=
crm_attribute: Invalid --list-options value 'asdf'. Allowed values: cluster
=#=#=#= End test: List all available cluster options (invalid value) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_attribute - List all available cluster options (invalid value) (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) =#=#=#=
* Passed: crm_attribute - Query the value of an attribute that does not exist
=#=#=#= Begin test: Configure something before erasing =#=#=#=
=#=#=#= Current cib after: Configure something before erasing =#=#=#=
=#=#=#= End test: Configure something before erasing - OK (0) =#=#=#=
* Passed: crm_attribute - Configure something before erasing
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Require --force for CIB erasure - Operation not safe (107) =#=#=#=
* Passed: cibadmin - Require --force for CIB erasure
=#=#=#= Begin test: Allow CIB erasure with --force =#=#=#=
=#=#=#= End test: Allow CIB erasure with --force - OK (0) =#=#=#=
* Passed: cibadmin - Allow CIB erasure with --force
=#=#=#= Begin test: Query CIB =#=#=#=
=#=#=#= Current cib after: Query CIB =#=#=#=
=#=#=#= End test: Query CIB - OK (0) =#=#=#=
* Passed: cibadmin - Query CIB
=#=#=#= Begin test: Set cluster option =#=#=#=
=#=#=#= Current cib after: Set cluster option =#=#=#=
=#=#=#= End test: Set cluster option - OK (0) =#=#=#=
* Passed: crm_attribute - Set cluster option
=#=#=#= Begin test: Query new cluster option =#=#=#=
=#=#=#= Current cib after: Query new cluster option =#=#=#=
=#=#=#= End test: Query new cluster option - OK (0) =#=#=#=
* Passed: cibadmin - Query new cluster option
=#=#=#= Begin test: Query cluster options =#=#=#=
=#=#=#= Current cib after: Query cluster options =#=#=#=
=#=#=#= End test: Query cluster options - OK (0) =#=#=#=
* Passed: cibadmin - Query cluster options
=#=#=#= Begin test: Set no-quorum policy =#=#=#=
=#=#=#= Current cib after: Set no-quorum policy =#=#=#=
=#=#=#= End test: Set no-quorum policy - OK (0) =#=#=#=
* Passed: crm_attribute - Set no-quorum policy
=#=#=#= Begin test: Delete nvpair =#=#=#=
=#=#=#= Current cib after: Delete nvpair =#=#=#=
=#=#=#= End test: Delete nvpair - OK (0) =#=#=#=
* Passed: cibadmin - Delete nvpair
=#=#=#= Begin test: Create operation should fail =#=#=#=
Call failed: File exists
=#=#=#= Current cib after: Create operation should fail =#=#=#=
=#=#=#= End test: Create operation should fail - Requested item already exists (108) =#=#=#=
* Passed: cibadmin - Create operation should fail
=#=#=#= Begin test: Modify cluster options section =#=#=#=
=#=#=#= Current cib after: Modify cluster options section =#=#=#=
=#=#=#= End test: Modify cluster options section - OK (0) =#=#=#=
* Passed: cibadmin - Modify cluster options section
=#=#=#= Begin test: Query updated cluster option =#=#=#=
=#=#=#= Current cib after: Query updated cluster option =#=#=#=
=#=#=#= End test: Query updated cluster option - OK (0) =#=#=#=
* Passed: cibadmin - Query updated cluster option
=#=#=#= Begin test: Set duplicate cluster option =#=#=#=
=#=#=#= Current cib after: Set duplicate cluster option =#=#=#=
=#=#=#= End test: Set duplicate cluster option - OK (0) =#=#=#=
* Passed: crm_attribute - Set duplicate cluster option
=#=#=#= Begin test: Setting multiply defined cluster option should fail =#=#=#=
crm_attribute: Please choose from one of the matches below and supply the 'id' with --attr-id
Multiple attributes match name=cluster-delay
Value: 60s (id=cib-bootstrap-options-cluster-delay)
Value: 40s (id=duplicate-cluster-delay)
=#=#=#= Current cib after: Setting multiply defined cluster option should fail =#=#=#=
=#=#=#= End test: Setting multiply defined cluster option should fail - Multiple items match request (109) =#=#=#=
* Passed: crm_attribute - Setting multiply defined cluster option should fail
=#=#=#= Begin test: Set cluster option with -s =#=#=#=
=#=#=#= Current cib after: Set cluster option with -s =#=#=#=
=#=#=#= End test: Set cluster option with -s - OK (0) =#=#=#=
* Passed: crm_attribute - Set cluster option with -s
=#=#=#= Begin test: Delete cluster option with -i =#=#=#=
Deleted crm_config option: id=(null) name=cluster-delay
=#=#=#= Current cib after: Delete cluster option with -i =#=#=#=
=#=#=#= End test: Delete cluster option with -i - OK (0) =#=#=#=
* Passed: crm_attribute - Delete cluster option with -i
=#=#=#= Begin test: Create node1 and bring it online =#=#=#=
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
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
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
Current cluster status:
* Full List of Resources:
* No resources
Performing Requested Modifications:
* Bringing node node1 online
Transition Summary:
Executing Cluster Transition:
Revised Cluster Status:
* Node List:
* Online: [ node1 ]
* Full List of Resources:
* No resources
=#=#=#= Current cib after: Create node1 and bring it online =#=#=#=
=#=#=#= End test: Create node1 and bring it online - OK (0) =#=#=#=
* Passed: crm_simulate - Create node1 and bring it online
=#=#=#= Begin test: Create node attribute =#=#=#=
=#=#=#= Current cib after: Create node attribute =#=#=#=
=#=#=#= End test: Create node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Create node attribute
=#=#=#= Begin test: Query new node attribute =#=#=#=
=#=#=#= Current cib after: Query new node attribute =#=#=#=
=#=#=#= End test: Query new node attribute - OK (0) =#=#=#=
* Passed: cibadmin - Query new node attribute
=#=#=#= Begin test: Create second node attribute =#=#=#=
=#=#=#= Current cib after: Create second node attribute =#=#=#=
=#=#=#= 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
=#=#=#= End test: Query node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Query node attributes by pattern
=#=#=#= Begin test: Update node attributes by pattern =#=#=#=
=#=#=#= Current cib after: Update node attributes by pattern =#=#=#=
=#=#=#= End test: Update node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Update node attributes by pattern
=#=#=#= Begin test: Delete node attributes by pattern =#=#=#=
Deleted nodes attribute: id=nodes-node1-rattr name=rattr
=#=#=#= Current cib after: Delete node attributes by pattern =#=#=#=
=#=#=#= End test: Delete node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Delete node attributes by pattern
=#=#=#= Begin test: Set a transient (fail-count) node attribute =#=#=#=
=#=#=#= Current cib after: Set a transient (fail-count) node attribute =#=#=#=
=#=#=#= End test: Set a transient (fail-count) node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Set a transient (fail-count) node attribute
=#=#=#= Begin test: Query a fail count =#=#=#=
scope=status name=fail-count-foo value=3
=#=#=#= Current cib after: Query a fail count =#=#=#=
=#=#=#= End test: Query a fail count - OK (0) =#=#=#=
* Passed: crm_failcount - Query a fail count
=#=#=#= Begin test: Show node attributes with crm_simulate =#=#=#=
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
Current cluster status:
* Node List:
* Online: [ node1 ]
* Full List of Resources:
* No resources
* Node Attributes:
* Node: node1:
* ram : 1024M
=#=#=#= End test: Show node attributes with crm_simulate - OK (0) =#=#=#=
* Passed: crm_simulate - Show node attributes with crm_simulate
=#=#=#= Begin test: Set a second transient node attribute =#=#=#=
=#=#=#= Current cib after: Set a second transient node attribute =#=#=#=
=#=#=#= 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
=#=#=#= 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 =#=#=#=
=#=#=#= Current cib after: Update transient node attributes by pattern =#=#=#=
=#=#=#= End test: Update transient node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Update transient node attributes by pattern
=#=#=#= Begin test: Delete transient node attributes by pattern =#=#=#=
Deleted status attribute: id=status-node1-fail-count-foo name=fail-count-foo
Deleted status attribute: id=status-node1-fail-count-bar name=fail-count-bar
=#=#=#= Current cib after: Delete transient node attributes by pattern =#=#=#=
=#=#=#= End test: Delete transient node attributes by pattern - OK (0) =#=#=#=
* Passed: crm_attribute - Delete transient node attributes by pattern
=#=#=#= Begin test: crm_attribute given invalid delete usage =#=#=#=
crm_attribute: Error: must specify attribute name or pattern to delete
=#=#=#= End test: crm_attribute given invalid delete usage - Incorrect usage (64) =#=#=#=
* Passed: crm_attribute - crm_attribute given invalid delete usage
=#=#=#= Begin test: Set a utilization node attribute =#=#=#=
=#=#=#= Current cib after: Set a utilization node attribute =#=#=#=
=#=#=#= 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
=#=#=#= End test: Query utilization node attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Query utilization node attribute
=#=#=#= Begin test: Digest calculation =#=#=#=
Digest: =#=#=#= Current cib after: Digest calculation =#=#=#=
=#=#=#= End test: Digest calculation - OK (0) =#=#=#=
* Passed: cibadmin - Digest calculation
=#=#=#= Begin test: Replace operation should fail =#=#=#=
Call failed: Update was older than existing configuration
=#=#=#= Current cib after: Replace operation should fail =#=#=#=
=#=#=#= 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
=#=#=#= Current cib after: Default standby value =#=#=#=
=#=#=#= End test: Default standby value - OK (0) =#=#=#=
* Passed: crm_standby - Default standby value
=#=#=#= Begin test: Set standby status =#=#=#=
=#=#=#= Current cib after: Set standby status =#=#=#=
=#=#=#= End test: Set standby status - OK (0) =#=#=#=
* Passed: crm_standby - Set standby status
=#=#=#= Begin test: Query standby value =#=#=#=
scope=nodes name=standby value=true
=#=#=#= Current cib after: Query standby value =#=#=#=
=#=#=#= End test: Query standby value - OK (0) =#=#=#=
* Passed: crm_standby - Query standby value
=#=#=#= Begin test: Delete standby value =#=#=#=
Deleted nodes attribute: id=nodes-node1-standby name=standby
=#=#=#= Current cib after: Delete standby value =#=#=#=
=#=#=#= End test: Delete standby value - OK (0) =#=#=#=
* Passed: crm_standby - Delete standby value
=#=#=#= Begin test: Create a resource =#=#=#=
=#=#=#= Current cib after: Create a resource =#=#=#=
=#=#=#= End test: Create a resource - OK (0) =#=#=#=
* Passed: cibadmin - Create a resource
=#=#=#= Begin test: crm_resource run with extra arguments =#=#=#=
crm_resource: non-option ARGV-elements:
[1 of 2] foo
[2 of 2] bar
=#=#=#= End test: crm_resource run with extra arguments - Incorrect usage (64) =#=#=#=
* Passed: crm_resource - crm_resource run with extra arguments
=#=#=#= Begin test: 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) =#=#=#=
* Passed: crm_resource - crm_resource given both -r and resource config
=#=#=#= Begin test: crm_resource given resource config with invalid action =#=#=#=
crm_resource: --class, --agent, and --provider can only be used with --validate and --force-*
=#=#=#= End test: crm_resource given resource config with invalid action - Incorrect usage (64) =#=#=#=
* Passed: crm_resource - crm_resource given resource config with invalid action
=#=#=#= Begin test: Create a resource meta attribute =#=#=#=
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
Set 'dummy' option: id=dummy-meta_attributes-is-managed set=dummy-meta_attributes name=is-managed value=false
=#=#=#= Current cib after: Create a resource meta attribute =#=#=#=
=#=#=#= End test: Create a resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute
=#=#=#= Begin test: Query a resource meta attribute =#=#=#=
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
false
=#=#=#= Current cib after: Query a resource meta attribute =#=#=#=
=#=#=#= End test: Query a resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Query a resource meta attribute
=#=#=#= Begin test: Remove a resource meta attribute =#=#=#=
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
Deleted 'dummy' option: id=dummy-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Remove a resource meta attribute =#=#=#=
=#=#=#= End test: Remove a resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Remove a resource meta attribute
=#=#=#= Begin test: Create another resource meta attribute =#=#=#=
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
=#=#=#= End test: Create another resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Create another resource meta attribute
=#=#=#= Begin test: Show why a resource is not running =#=#=#=
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
=#=#=#= End test: Show why a resource is not running - OK (0) =#=#=#=
* Passed: crm_resource - Show why a resource is not running
=#=#=#= Begin test: Remove another resource meta attribute =#=#=#=
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
=#=#=#= End test: Remove another resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Remove another resource meta attribute
=#=#=#= Begin test: Get a non-existent attribute from a resource element with output-as=xml =#=#=#=
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
Attribute 'nonexistent' not found for 'dummy'
=#=#=#= End test: Get a non-existent attribute from a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Get a non-existent attribute from a resource element with output-as=xml
=#=#=#= Begin test: Get a non-existent attribute from a resource element without output-as=xml =#=#=#=
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
Attribute 'nonexistent' not found for 'dummy'
=#=#=#= Current cib after: Get a non-existent attribute from a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Get a non-existent attribute from a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Get a non-existent attribute from a resource element without output-as=xml
=#=#=#= Begin test: Get an existent attribute from a resource element with output-as=xml =#=#=#=
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
ocf
=#=#=#= End test: Get an existent attribute from a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Get an existent attribute from a resource element with output-as=xml
=#=#=#= Begin test: Get an existent attribute from a resource element without output-as=xml =#=#=#=
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
ocf
=#=#=#= Current cib after: Get an existent attribute from a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Get an existent attribute from a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Get an existent attribute from a resource element without output-as=xml
=#=#=#= Begin test: Set a non-existent attribute for a resource element with output-as=xml =#=#=#=
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
=#=#=#= Current cib after: Set a non-existent attribute for a resource element with output-as=xml =#=#=#=
=#=#=#= End test: Set a non-existent attribute for a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Set a non-existent attribute for a resource element with output-as=xml
=#=#=#= Begin test: Set an existent attribute for a resource element with output-as=xml =#=#=#=
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
=#=#=#= Current cib after: Set an existent attribute for a resource element with output-as=xml =#=#=#=
=#=#=#= End test: Set an existent attribute for a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Set an existent attribute for a resource element with output-as=xml
=#=#=#= Begin test: Delete an existent attribute for a resource element with output-as=xml =#=#=#=
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
=#=#=#= Current cib after: Delete an existent attribute for a resource element with output-as=xml =#=#=#=
=#=#=#= End test: Delete an existent attribute for a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Delete an existent attribute for a resource element with output-as=xml
=#=#=#= Begin test: Delete a non-existent attribute for a resource element with output-as=xml =#=#=#=
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
=#=#=#= Current cib after: Delete a non-existent attribute for a resource element with output-as=xml =#=#=#=
=#=#=#= End test: Delete a non-existent attribute for a resource element with output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Delete a non-existent attribute for a resource element with output-as=xml
=#=#=#= Begin test: Set a non-existent attribute for a resource element without output-as=xml =#=#=#=
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
Set attribute: name=description value=test_description
=#=#=#= Current cib after: Set a non-existent attribute for a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Set a non-existent attribute for a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Set a non-existent attribute for a resource element without output-as=xml
=#=#=#= Begin test: Set an existent attribute for a resource element without output-as=xml =#=#=#=
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
Set attribute: name=description value=test_description
=#=#=#= Current cib after: Set an existent attribute for a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Set an existent attribute for a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Set an existent attribute for a resource element without output-as=xml
=#=#=#= Begin test: Delete an existent attribute for a resource element without output-as=xml =#=#=#=
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
Deleted attribute: description
=#=#=#= Current cib after: Delete an existent attribute for a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Delete an existent attribute for a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Delete an existent attribute for a resource element without output-as=xml
=#=#=#= Begin test: Delete a non-existent attribute for a resource element without output-as=xml =#=#=#=
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
Deleted attribute: description
=#=#=#= Current cib after: Delete a non-existent attribute for a resource element without output-as=xml =#=#=#=
=#=#=#= End test: Delete a non-existent attribute for a resource element without output-as=xml - OK (0) =#=#=#=
* Passed: crm_resource - Delete a non-existent attribute for a resource element without output-as=xml
=#=#=#= Begin test: Create a resource attribute =#=#=#=
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
Set 'dummy' option: id=dummy-instance_attributes-delay set=dummy-instance_attributes name=delay value=10s
=#=#=#= Current cib after: Create a resource attribute =#=#=#=
=#=#=#= End test: Create a resource attribute - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource attribute
=#=#=#= Begin test: List the configured resources =#=#=#=
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
Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Stopped
=#=#=#= Current cib after: List the configured resources =#=#=#=
=#=#=#= End test: List the configured resources - OK (0) =#=#=#=
* Passed: crm_resource - List the configured resources
=#=#=#= Begin test: List the configured resources in XML =#=#=#=
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
=#=#=#= End test: List the configured resources in XML - OK (0) =#=#=#=
* Passed: crm_resource - List the configured resources in XML
=#=#=#= Begin test: Implicitly list the configured resources =#=#=#=
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
Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Stopped
=#=#=#= End test: Implicitly list the configured resources - OK (0) =#=#=#=
* Passed: crm_resource - Implicitly list the configured resources
=#=#=#= Begin test: List IDs of instantiated resources =#=#=#=
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
dummy
=#=#=#= End test: List IDs of instantiated resources - OK (0) =#=#=#=
* Passed: crm_resource - List IDs of instantiated resources
=#=#=#= Begin test: Show XML configuration of resource =#=#=#=
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
dummy (ocf:pacemaker:Dummy): Stopped
Resource XML:
=#=#=#= End test: Show XML configuration of resource - OK (0) =#=#=#=
* Passed: crm_resource - Show XML configuration of resource
=#=#=#= Begin test: Show XML configuration of resource, output as XML =#=#=#=
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
]]>
=#=#=#= End test: Show XML configuration of resource, output as XML - OK (0) =#=#=#=
* Passed: crm_resource - Show XML configuration of resource, output as XML
=#=#=#= Begin test: Require a destination when migrating a resource that is stopped =#=#=#=
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
crm_resource: Resource 'dummy' not moved: active in 0 locations.
To prevent 'dummy' from running on a specific location, specify a node.
=#=#=#= Current cib after: Require a destination when migrating a resource that is stopped =#=#=#=
=#=#=#= End test: Require a destination when migrating a resource that is stopped - Incorrect usage (64) =#=#=#=
* Passed: crm_resource - Require a destination when migrating a resource that is stopped
=#=#=#= Begin test: Don't support migration to non-existent locations =#=#=#=
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
crm_resource: Node 'i.do.not.exist' not found
Error performing operation: No such object
=#=#=#= Current cib after: Don't support migration to non-existent locations =#=#=#=
=#=#=#= End test: Don't support migration to non-existent locations - No such object (105) =#=#=#=
* Passed: crm_resource - Don't support migration to non-existent locations
=#=#=#= Begin test: Create a fencing resource =#=#=#=
=#=#=#= Current cib after: Create a fencing resource =#=#=#=
=#=#=#= End test: Create a fencing resource - OK (0) =#=#=#=
* Passed: cibadmin - Create a fencing resource
=#=#=#= Begin test: Bring resources online =#=#=#=
Current cluster status:
* Node List:
* Online: [ node1 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Stopped
* Fence (stonith:fence_true): Stopped
Transition Summary:
* Start dummy ( node1 )
* Start Fence ( node1 )
Executing Cluster Transition:
* Resource action: dummy monitor on node1
* Resource action: Fence monitor on node1
* Resource action: dummy start on node1
* Resource action: Fence start on node1
Revised Cluster Status:
* Node List:
* Online: [ node1 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Started node1
* Fence (stonith:fence_true): Started node1
=#=#=#= Current cib after: Bring resources online =#=#=#=
=#=#=#= End test: Bring resources online - OK (0) =#=#=#=
* Passed: crm_simulate - Bring resources online
=#=#=#= Begin test: Try to move a resource to its existing location =#=#=#=
crm_resource: Error performing operation: Requested item already exists
=#=#=#= Current cib after: Try to move a resource to its existing location =#=#=#=
=#=#=#= End test: Try to move a resource to its existing location - Requested item already exists (108) =#=#=#=
* Passed: crm_resource - Try to move a resource to its existing location
=#=#=#= Begin test: Try to move a resource that doesn't exist =#=#=#=
crm_resource: Resource 'xyz' not found
Error performing operation: No such object
=#=#=#= End test: Try to move a resource that doesn't exist - No such object (105) =#=#=#=
* 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 be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Move a resource from its existing location =#=#=#=
=#=#=#= End test: Move a resource from its existing location - OK (0) =#=#=#=
* Passed: crm_resource - Move a resource from its existing location
=#=#=#= Begin test: Clear out constraints generated by --move =#=#=#=
Removing constraint: cli-ban-dummy-on-node1
=#=#=#= Current cib after: Clear out constraints generated by --move =#=#=#=
=#=#=#= End test: Clear out constraints generated by --move - OK (0) =#=#=#=
* Passed: crm_resource - Clear out constraints generated by --move
=#=#=#= Begin test: Default ticket granted state =#=#=#=
false
=#=#=#= Current cib after: Default ticket granted state =#=#=#=
=#=#=#= End test: Default ticket granted state - OK (0) =#=#=#=
* Passed: crm_ticket - Default ticket granted state
=#=#=#= Begin test: Set ticket granted state =#=#=#=
=#=#=#= Current cib after: Set ticket granted state =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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:
=#=#=#= End test: Query ticket state - OK (0) =#=#=#=
* Passed: crm_ticket - Query ticket state
=#=#=#= Begin test: Query ticket state, outputting as xml =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Query ticket granted state - OK (0) =#=#=#=
* Passed: crm_ticket - Query ticket granted state
=#=#=#= Begin test: Query ticket granted state, outputting as xml =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Delete ticket granted state - OK (0) =#=#=#=
* Passed: crm_ticket - Delete ticket granted state
=#=#=#= Begin test: Make a ticket standby =#=#=#=
=#=#=#= Current cib after: Make a ticket standby =#=#=#=
=#=#=#= End test: Make a ticket standby - OK (0) =#=#=#=
* Passed: crm_ticket - Make a ticket standby
=#=#=#= Begin test: Query ticket standby state =#=#=#=
true
=#=#=#= Current cib after: Query ticket standby state =#=#=#=
=#=#=#= End test: Query ticket standby state - OK (0) =#=#=#=
* Passed: crm_ticket - Query ticket standby state
=#=#=#= Begin test: Activate a ticket =#=#=#=
=#=#=#= Current cib after: Activate a ticket =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Delete ticket standby state - OK (0) =#=#=#=
* Passed: cibadmin - Delete ticket standby state
=#=#=#= Begin test: Query ticket constraints =#=#=#=
Constraints XML:
=#=#=#= End test: Query ticket constraints - OK (0) =#=#=#=
* Passed: crm_ticket - Query ticket constraints
=#=#=#= Begin test: Query ticket constraints, outputting as xml =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Ban a resource on unknown node - No such object (105) =#=#=#=
* Passed: crm_resource - Ban a resource on unknown node
=#=#=#= Begin test: Create two more nodes and bring them online =#=#=#=
Current cluster status:
* Node List:
* Online: [ node1 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Started node1
* Fence (stonith:fence_true): Started node1
Performing Requested Modifications:
* Bringing node node2 online
* Bringing node node3 online
Transition Summary:
* Move Fence ( node1 -> node2 )
Executing Cluster Transition:
* Resource action: dummy monitor on node3
* Resource action: dummy monitor on node2
* Resource action: Fence stop on node1
* Resource action: Fence monitor on node3
* Resource action: Fence monitor on node2
* Resource action: Fence start on node2
Revised Cluster Status:
* Node List:
* Online: [ node1 node2 node3 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Started node1
* Fence (stonith:fence_true): Started node2
=#=#=#= Current cib after: Create two more nodes and bring them online =#=#=#=
=#=#=#= End test: Create two more nodes and bring them online - OK (0) =#=#=#=
* 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 be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Ban dummy from node1 =#=#=#=
=#=#=#= End test: Ban dummy from node1 - OK (0) =#=#=#=
* Passed: crm_resource - Ban dummy from node1
=#=#=#= Begin test: Show where a resource is running =#=#=#=
resource dummy is running on: node1
=#=#=#= End test: Show where a resource is running - OK (0) =#=#=#=
* Passed: crm_resource - Show where a resource is running
=#=#=#= Begin test: Show constraints on a resource =#=#=#=
Locations:
* Node node1 (score=-INFINITY, id=cli-ban-dummy-on-node1, rsc=dummy)
=#=#=#= End test: Show constraints on a resource - OK (0) =#=#=#=
* Passed: crm_resource - Show constraints on a resource
=#=#=#= Begin test: Ban dummy from node2 =#=#=#=
=#=#=#= Current cib after: Ban dummy from node2 =#=#=#=
=#=#=#= End test: Ban dummy from node2 - OK (0) =#=#=#=
* Passed: crm_resource - Ban dummy from node2
=#=#=#= Begin test: Relocate resources due to ban =#=#=#=
Current cluster status:
* Node List:
* Online: [ node1 node2 node3 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Started node1
* Fence (stonith:fence_true): Started node2
Transition Summary:
* Move dummy ( node1 -> node3 )
Executing Cluster Transition:
* Resource action: dummy stop on node1
* Resource action: dummy start on node3
Revised Cluster Status:
* Node List:
* Online: [ node1 node2 node3 ]
* Full List of Resources:
* dummy (ocf:pacemaker:Dummy): Started node3
* Fence (stonith:fence_true): Started node2
=#=#=#= Current cib after: Relocate resources due to ban =#=#=#=
=#=#=#= End test: Relocate resources due to ban - OK (0) =#=#=#=
* Passed: crm_simulate - Relocate resources due to ban
=#=#=#= Begin test: Move dummy to node1 =#=#=#=
=#=#=#= Current cib after: Move dummy to node1 =#=#=#=
=#=#=#= End test: Move dummy to node1 - OK (0) =#=#=#=
* Passed: crm_resource - Move dummy to node1
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Clear implicit constraints for dummy on node2 - OK (0) =#=#=#=
* Passed: crm_resource - Clear implicit constraints for dummy on node2
=#=#=#= Begin test: Drop the status section =#=#=#=
=#=#=#= End test: Drop the status section - OK (0) =#=#=#=
* Passed: cibadmin - Drop the status section
=#=#=#= Begin test: Create a clone =#=#=#=
=#=#=#= End test: Create a clone - OK (0) =#=#=#=
* Passed: cibadmin - Create a clone
=#=#=#= Begin test: Create a resource meta attribute =#=#=#=
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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute in the primitive - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute in the primitive
=#=#=#= Begin test: Update resource meta attribute with duplicates =#=#=#=
Multiple attributes match name=is-managed
Value: false (id=test-primitive-meta_attributes-is-managed)
Value: false (id=test-clone-meta_attributes-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 =#=#=#=
=#=#=#= End test: Update resource meta attribute with duplicates - OK (0) =#=#=#=
* Passed: crm_resource - Update resource meta attribute with duplicates
=#=#=#= 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) =#=#=#=
=#=#=#= End test: Update resource meta attribute with duplicates (force clone) - OK (0) =#=#=#=
* Passed: crm_resource - Update resource meta attribute with duplicates (force clone)
=#=#=#= Begin test: Update child resource meta attribute with duplicates =#=#=#=
Multiple attributes match name=is-managed
Value: true (id=test-primitive-meta_attributes-is-managed)
Value: true (id=test-clone-meta_attributes-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 =#=#=#=
=#=#=#= End test: Update child resource meta attribute with duplicates - OK (0) =#=#=#=
* Passed: crm_resource - Update child resource meta attribute with duplicates
=#=#=#= Begin test: Delete resource meta attribute with duplicates =#=#=#=
Multiple attributes match name=is-managed
Value: false (id=test-primitive-meta_attributes-is-managed)
Value: true (id=test-clone-meta_attributes-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 =#=#=#=
=#=#=#= End test: Delete resource meta attribute with duplicates - OK (0) =#=#=#=
* Passed: crm_resource - Delete resource meta attribute with duplicates
=#=#=#= Begin test: Delete resource meta attribute in parent =#=#=#=
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 =#=#=#=
=#=#=#= End test: Delete resource meta attribute in parent - OK (0) =#=#=#=
* Passed: crm_resource - Delete resource meta attribute in parent
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute in the primitive - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute in the primitive
=#=#=#= Begin test: Update existing resource meta attribute =#=#=#=
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 =#=#=#=
=#=#=#= End test: Update existing resource meta attribute - OK (0) =#=#=#=
* Passed: crm_resource - Update existing resource meta attribute
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute in the parent - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute in the parent
=#=#=#= Begin test: Copy resources =#=#=#=
=#=#=#= End test: Copy resources - OK (0) =#=#=#=
* Passed: cibadmin - Copy resources
=#=#=#= 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) =#=#=#=
=#=#=#= End test: Delete resource parent meta attribute (force) - OK (0) =#=#=#=
* Passed: crm_resource - Delete resource parent meta attribute (force)
=#=#=#= Begin test: Restore duplicates =#=#=#=
=#=#=#= Current cib after: Restore duplicates =#=#=#=
=#=#=#= End test: Restore duplicates - OK (0) =#=#=#=
* Passed: cibadmin - Restore duplicates
=#=#=#= Begin test: Delete resource child meta attribute =#=#=#=
Multiple attributes match name=is-managed
Value: true (id=test-primitive-meta_attributes-is-managed)
Value: true (id=test-clone-meta_attributes-is-managed)
Deleted 'test-primitive' option: id=test-primitive-meta_attributes-is-managed name=is-managed
=#=#=#= Current cib after: Delete resource child meta attribute =#=#=#=
=#=#=#= End test: Delete resource child meta attribute - OK (0) =#=#=#=
* 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 =#=#=#=
=#=#=#= End test: Create the dummy-group resource group - OK (0) =#=#=#=
* Passed: cibadmin - Create the dummy-group resource group
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute in dummy1 - OK (0) =#=#=#=
* Passed: crm_resource - Create a resource meta attribute in dummy1
=#=#=#= Begin test: Create a resource meta attribute in dummy-group =#=#=#=
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 =#=#=#=
=#=#=#= End test: Create a resource meta attribute in dummy-group - OK (0) =#=#=#=
* 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 =#=#=#=
=#=#=#= End test: Delete the dummy-group resource group - OK (0) =#=#=#=
* Passed: cibadmin - Delete the dummy-group resource group
=#=#=#= Begin test: Specify a lifetime when moving a resource =#=#=#=
Migration will take effect until:
=#=#=#= Current cib after: Specify a lifetime when moving a resource =#=#=#=
=#=#=#= End test: Specify a lifetime when moving a resource - OK (0) =#=#=#=
* 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 =#=#=#=
=#=#=#= End test: Try to move a resource previously moved with a lifetime - OK (0) =#=#=#=
* Passed: crm_resource - Try to move a resource previously moved with a lifetime
=#=#=#= 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 be the case even if node1 is the last node in the cluster
=#=#=#= Current cib after: Ban dummy from node1 for a short time =#=#=#=
=#=#=#= End test: Ban dummy from node1 for a short time - OK (0) =#=#=#=
* Passed: crm_resource - Ban dummy from node1 for a short time
=#=#=#= Begin test: Remove expired constraints =#=#=#=
Removing constraint: cli-ban-dummy-on-node1
=#=#=#= Current cib after: Remove expired constraints =#=#=#=
=#=#=#= End test: Remove expired constraints - OK (0) =#=#=#=
* Passed: crm_resource - Remove expired constraints
=#=#=#= Begin test: Clear all implicit constraints for dummy =#=#=#=
Removing constraint: cli-prefer-dummy
=#=#=#= Current cib after: Clear all implicit constraints for dummy =#=#=#=
=#=#=#= End test: Clear all implicit constraints for dummy - OK (0) =#=#=#=
* Passed: crm_resource - Clear all implicit constraints for dummy
=#=#=#= Begin test: Set a node health strategy =#=#=#=
=#=#=#= Current cib after: Set a node health strategy =#=#=#=
=#=#=#= End test: Set a node health strategy - OK (0) =#=#=#=
* Passed: crm_attribute - Set a node health strategy
=#=#=#= Begin test: Set a node health attribute =#=#=#=
=#=#=#= Current cib after: Set a node health attribute =#=#=#=
=#=#=#= End test: Set a node health attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Set a node health attribute
=#=#=#= Begin test: Show why a resource is not running on an unhealthy node =#=#=#=
=#=#=#= End test: Show why a resource is not running on an unhealthy node - OK (0) =#=#=#=
* 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 =#=#=#=
=#=#=#= End test: Delete a resource - OK (0) =#=#=#=
* Passed: crm_resource - Delete a resource
=#=#=#= Begin test: Create an XML patchset =#=#=#=
=#=#=#= End test: Create an XML patchset - Error occurred (1) =#=#=#=
* Passed: crm_diff - Create an XML patchset
=#=#=#= Begin test: Check locations and constraints for prim1 =#=#=#=
=#=#=#= End test: Check locations and constraints for prim1 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim1
=#=#=#= Begin test: Recursively check locations and constraints for prim1 =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim1 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim1
=#=#=#= Begin test: Check locations and constraints for prim1 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim1 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim1 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim1 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim1 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim1 in XML
=#=#=#= Begin test: Check locations and constraints for prim2 =#=#=#=
Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
Resources prim2 is colocated with:
* prim3 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
=#=#=#= End test: Check locations and constraints for prim2 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim2
=#=#=#= Begin test: Recursively check locations and constraints for prim2 =#=#=#=
Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
Resources prim2 is colocated with:
* prim3 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
* Resources prim3 is colocated with:
* prim4 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
* Resources prim4 is colocated with:
* prim5 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim2 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim2
=#=#=#= Begin test: Check locations and constraints for prim2 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim2 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim2 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim2 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim2 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim2 in XML
=#=#=#= Begin test: Check locations and constraints for prim3 =#=#=#=
Resources colocated with prim3:
* prim2 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
* Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
Resources prim3 is colocated with:
* prim4 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
=#=#=#= End test: Check locations and constraints for prim3 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim3
=#=#=#= Begin test: Recursively check locations and constraints for prim3 =#=#=#=
Resources colocated with prim3:
* prim2 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
* Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
Resources prim3 is colocated with:
* prim4 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
* Resources prim4 is colocated with:
* prim5 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim3 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim3
=#=#=#= Begin test: Check locations and constraints for prim3 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim3 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim3 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim3 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim3 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim3 in XML
=#=#=#= Begin test: Check locations and constraints for prim4 =#=#=#=
Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
Resources colocated with prim4:
* prim10 (score=INFINITY, id=colocation-prim10-prim4-INFINITY)
* prim3 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
Resources prim4 is colocated with:
* prim5 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
=#=#=#= End test: Check locations and constraints for prim4 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim4
=#=#=#= Begin test: Recursively check locations and constraints for prim4 =#=#=#=
Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
Resources colocated with prim4:
* prim10 (score=INFINITY, id=colocation-prim10-prim4-INFINITY)
* prim3 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
* Resources colocated with prim3:
* prim2 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
* Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
Resources prim4 is colocated with:
* prim5 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim4 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim4
=#=#=#= Begin test: Check locations and constraints for prim4 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim4 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim4 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim4 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim4 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim4 in XML
=#=#=#= Begin test: Check locations and constraints for prim5 =#=#=#=
Resources colocated with prim5:
* prim4 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
=#=#=#= End test: Check locations and constraints for prim5 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim5
=#=#=#= Begin test: Recursively check locations and constraints for prim5 =#=#=#=
Resources colocated with prim5:
* prim4 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
* Resources colocated with prim4:
* prim10 (score=INFINITY, id=colocation-prim10-prim4-INFINITY)
* prim3 (score=INFINITY, id=colocation-prim3-prim4-INFINITY)
* Resources colocated with prim3:
* prim2 (score=INFINITY, id=colocation-prim2-prim3-INFINITY)
* Locations:
* Node cluster01 (score=INFINITY, id=prim2-on-cluster1, rsc=prim2)
=#=#=#= End test: Recursively check locations and constraints for prim5 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim5
=#=#=#= Begin test: Check locations and constraints for prim5 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim5 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim5 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim5 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim5 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim5 in XML
=#=#=#= Begin test: Check locations and constraints for prim6 =#=#=#=
Locations:
* Node cluster02 (score=-INFINITY, id=prim6-not-on-cluster2, rsc=prim6)
=#=#=#= End test: Check locations and constraints for prim6 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim6
=#=#=#= Begin test: Recursively check locations and constraints for prim6 =#=#=#=
Locations:
* Node cluster02 (score=-INFINITY, id=prim6-not-on-cluster2, rsc=prim6)
=#=#=#= End test: Recursively check locations and constraints for prim6 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim6
=#=#=#= Begin test: Check locations and constraints for prim6 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim6 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim6 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim6 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim6 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim6 in XML
=#=#=#= Begin test: Check locations and constraints for prim7 =#=#=#=
Resources prim7 is colocated with:
* group (score=INFINITY, id=colocation-prim7-group-INFINITY)
=#=#=#= End test: Check locations and constraints for prim7 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim7
=#=#=#= Begin test: Recursively check locations and constraints for prim7 =#=#=#=
Resources prim7 is colocated with:
* group (score=INFINITY, id=colocation-prim7-group-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim7 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim7
=#=#=#= Begin test: Check locations and constraints for prim7 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim7 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim7 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim7 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim7 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim7 in XML
=#=#=#= Begin test: Check locations and constraints for prim8 =#=#=#=
Resources prim8 is colocated with:
* gr2 (score=INFINITY, id=colocation-prim8-gr2-INFINITY)
=#=#=#= End test: Check locations and constraints for prim8 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim8
=#=#=#= Begin test: Recursively check locations and constraints for prim8 =#=#=#=
Resources prim8 is colocated with:
* gr2 (score=INFINITY, id=colocation-prim8-gr2-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim8 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim8
=#=#=#= Begin test: Check locations and constraints for prim8 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim8 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim8 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim8 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim8 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim8 in XML
=#=#=#= Begin test: Check locations and constraints for prim9 =#=#=#=
Resources prim9 is colocated with:
* clone (score=INFINITY, id=colocation-prim9-clone-INFINITY)
=#=#=#= End test: Check locations and constraints for prim9 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim9
=#=#=#= Begin test: Recursively check locations and constraints for prim9 =#=#=#=
Resources prim9 is colocated with:
* clone (score=INFINITY, id=colocation-prim9-clone-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim9 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim9
=#=#=#= Begin test: Check locations and constraints for prim9 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim9 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim9 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim9 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim9 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim9 in XML
=#=#=#= Begin test: Check locations and constraints for prim10 =#=#=#=
Resources prim10 is colocated with:
* prim4 (score=INFINITY, id=colocation-prim10-prim4-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
=#=#=#= End test: Check locations and constraints for prim10 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim10
=#=#=#= Begin test: Recursively check locations and constraints for prim10 =#=#=#=
Resources prim10 is colocated with:
* prim4 (score=INFINITY, id=colocation-prim10-prim4-INFINITY)
* Locations:
* Node cluster02 (score=INFINITY, id=prim4-on-cluster2, rsc=prim4)
* Resources prim4 is colocated with:
* prim5 (score=INFINITY, id=colocation-prim4-prim5-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for prim10 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim10
=#=#=#= Begin test: Check locations and constraints for prim10 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim10 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim10 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim10 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim10 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim10 in XML
=#=#=#= Begin test: Check locations and constraints for prim11 =#=#=#=
Resources colocated with prim11:
* prim13 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
Resources prim11 is colocated with:
* prim12 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
=#=#=#= End test: Check locations and constraints for prim11 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim11
=#=#=#= Begin test: Recursively check locations and constraints for prim11 =#=#=#=
Resources colocated with prim11:
* prim13 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
* Resources colocated with prim13:
* prim12 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
* Resources colocated with prim12:
* prim11 (id=colocation-prim11-prim12-INFINITY - loop)
Resources prim11 is colocated with:
* prim12 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
* Resources prim12 is colocated with:
* prim13 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
* Resources prim13 is colocated with:
* prim11 (id=colocation-prim13-prim11-INFINITY - loop)
=#=#=#= End test: Recursively check locations and constraints for prim11 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim11
=#=#=#= Begin test: Check locations and constraints for prim11 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim11 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim11 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim11 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim11 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim11 in XML
=#=#=#= Begin test: Check locations and constraints for prim12 =#=#=#=
Resources colocated with prim12:
* prim11 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
Resources prim12 is colocated with:
* prim13 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
=#=#=#= End test: Check locations and constraints for prim12 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim12
=#=#=#= Begin test: Recursively check locations and constraints for prim12 =#=#=#=
Resources colocated with prim12:
* prim11 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
* Resources colocated with prim11:
* prim13 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
* Resources colocated with prim13:
* prim12 (id=colocation-prim12-prim13-INFINITY - loop)
Resources prim12 is colocated with:
* prim13 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
* Resources prim13 is colocated with:
* prim11 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
* Resources prim11 is colocated with:
* prim12 (id=colocation-prim11-prim12-INFINITY - loop)
=#=#=#= End test: Recursively check locations and constraints for prim12 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim12
=#=#=#= Begin test: Check locations and constraints for prim12 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim12 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim12 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim12 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim12 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim12 in XML
=#=#=#= Begin test: Check locations and constraints for prim13 =#=#=#=
Resources colocated with prim13:
* prim12 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
Resources prim13 is colocated with:
* prim11 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
=#=#=#= End test: Check locations and constraints for prim13 - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim13
=#=#=#= Begin test: Recursively check locations and constraints for prim13 =#=#=#=
Resources colocated with prim13:
* prim12 (score=INFINITY, id=colocation-prim12-prim13-INFINITY)
* Resources colocated with prim12:
* prim11 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
* Resources colocated with prim11:
* prim13 (id=colocation-prim13-prim11-INFINITY - loop)
Resources prim13 is colocated with:
* prim11 (score=INFINITY, id=colocation-prim13-prim11-INFINITY)
* Resources prim11 is colocated with:
* prim12 (score=INFINITY, id=colocation-prim11-prim12-INFINITY)
* Resources prim12 is colocated with:
* prim13 (id=colocation-prim12-prim13-INFINITY - loop)
=#=#=#= End test: Recursively check locations and constraints for prim13 - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim13
=#=#=#= Begin test: Check locations and constraints for prim13 in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for prim13 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for prim13 in XML
=#=#=#= Begin test: Recursively check locations and constraints for prim13 in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for prim13 in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for prim13 in XML
=#=#=#= Begin test: Check locations and constraints for group =#=#=#=
Resources colocated with group:
* prim7 (score=INFINITY, id=colocation-prim7-group-INFINITY)
=#=#=#= End test: Check locations and constraints for group - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for group
=#=#=#= Begin test: Recursively check locations and constraints for group =#=#=#=
Resources colocated with group:
* prim7 (score=INFINITY, id=colocation-prim7-group-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for group - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for group
=#=#=#= Begin test: Check locations and constraints for group in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for group in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for group in XML
=#=#=#= Begin test: Recursively check locations and constraints for group in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for group in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for group in XML
=#=#=#= Begin test: Check locations and constraints for clone =#=#=#=
Resources colocated with clone:
* prim9 (score=INFINITY, id=colocation-prim9-clone-INFINITY)
=#=#=#= End test: Check locations and constraints for clone - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for clone
=#=#=#= Begin test: Recursively check locations and constraints for clone =#=#=#=
Resources colocated with clone:
* prim9 (score=INFINITY, id=colocation-prim9-clone-INFINITY)
=#=#=#= End test: Recursively check locations and constraints for clone - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for clone
=#=#=#= Begin test: Check locations and constraints for clone in XML =#=#=#=
=#=#=#= End test: Check locations and constraints for clone in XML - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for clone in XML
=#=#=#= Begin test: Recursively check locations and constraints for clone in XML =#=#=#=
=#=#=#= End test: Recursively check locations and constraints for clone in XML - OK (0) =#=#=#=
* Passed: crm_resource - Recursively check locations and constraints for clone in XML
=#=#=#= Begin test: Check locations and constraints for group member (referring to group) =#=#=#=
Resources colocated with group:
* prim7 (score=INFINITY, id=colocation-prim7-group-INFINITY)
=#=#=#= End test: Check locations and constraints for group member (referring to group) - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for group member (referring to group)
=#=#=#= Begin test: Check locations and constraints for group member (without referring to group) =#=#=#=
Resources colocated with gr2:
* prim8 (score=INFINITY, id=colocation-prim8-gr2-INFINITY)
=#=#=#= End test: Check locations and constraints for group member (without referring to group) - OK (0) =#=#=#=
* Passed: crm_resource - Check locations and constraints for group member (without referring to group)
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
=#=#=#= 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 =#=#=#=
Set 'group' option: id=group-meta_attributes-target-role set=group-meta_attributes name=target-role value=Stopped
Set 'prim7' option: id=prim7-meta_attributes-target-role set=prim7-meta_attributes name=target-role value=Stopped
=#=#=#= 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
=#=#=#= 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 =#=#=#=
=#=#=#= End test: Show resource digests - OK (0) =#=#=#=
* Passed: crm_resource - Show resource digests
=#=#=#= Begin test: Show resource digests with overrides =#=#=#=
=#=#=#= End test: Show resource digests with overrides - OK (0) =#=#=#=
* Passed: crm_resource - Show resource digests with overrides
=#=#=#= Begin test: Show resource operations =#=#=#=
rsc1 (ocf:pacemaker:Dummy): Started: rsc1_monitor_0 (node=node4, call=136, rc=7, exec=28ms): complete
Fencing (stonith:fence_xvm): Started: Fencing_monitor_0 (node=node4, call=5, rc=7, exec=2ms): complete
rsc1 (ocf:pacemaker:Dummy): Started: rsc1_monitor_0 (node=node2, call=101, rc=7, exec=45ms): complete
Fencing (stonith:fence_xvm): Started: Fencing_monitor_0 (node=node2, call=5, rc=7, exec=4ms): complete
Fencing (stonith:fence_xvm): Started: Fencing_monitor_0 (node=node3, call=5, rc=7, exec=24ms): complete
rsc1 (ocf:pacemaker:Dummy): Started: rsc1_monitor_0 (node=node5, call=99, rc=193, exec=27ms): pending
Fencing (stonith:fence_xvm): Started: Fencing_monitor_0 (node=node5, call=5, rc=7, exec=14ms): complete
rsc1 (ocf:pacemaker:Dummy): Started: rsc1_start_0 (node=node1, call=104, rc=0, exec=22ms): complete
rsc1 (ocf:pacemaker:Dummy): Started: rsc1_monitor_10000 (node=node1, call=106, rc=0, exec=20ms): complete
Fencing (stonith:fence_xvm): Started: Fencing_start_0 (node=node1, call=10, rc=0, exec=59ms): complete
Fencing (stonith:fence_xvm): Started: Fencing_monitor_120000 (node=node1, call=12, rc=0, exec=70ms): complete
=#=#=#= End test: Show resource operations - OK (0) =#=#=#=
* Passed: crm_resource - Show resource operations
=#=#=#= Begin test: Show resource operations (XML) =#=#=#=
=#=#=#= End test: Show resource operations (XML) - OK (0) =#=#=#=
* Passed: crm_resource - Show resource operations (XML)
=#=#=#= Begin test: List all nodes =#=#=#=
cluster node: overcloud-controller-0 (1)
cluster node: overcloud-controller-1 (2)
cluster node: overcloud-controller-2 (3)
cluster node: overcloud-galera-0 (4)
cluster node: overcloud-galera-1 (5)
cluster node: overcloud-galera-2 (6)
guest node: lxc1 (lxc1)
guest node: lxc2 (lxc2)
remote node: overcloud-rabbit-0 (overcloud-rabbit-0)
remote node: overcloud-rabbit-1 (overcloud-rabbit-1)
remote node: overcloud-rabbit-2 (overcloud-rabbit-2)
=#=#=#= End test: List all nodes - OK (0) =#=#=#=
* Passed: crmadmin - List all nodes
=#=#=#= Begin test: Minimally list all nodes =#=#=#=
overcloud-controller-0
overcloud-controller-1
overcloud-controller-2
overcloud-galera-0
overcloud-galera-1
overcloud-galera-2
lxc1
lxc2
overcloud-rabbit-0
overcloud-rabbit-1
overcloud-rabbit-2
=#=#=#= End test: Minimally list all nodes - OK (0) =#=#=#=
* Passed: crmadmin - Minimally list all nodes
=#=#=#= Begin test: List all nodes as bash exports =#=#=#=
export overcloud-controller-0=1
export overcloud-controller-1=2
export overcloud-controller-2=3
export overcloud-galera-0=4
export overcloud-galera-1=5
export overcloud-galera-2=6
export lxc1=lxc1
export lxc2=lxc2
export overcloud-rabbit-0=overcloud-rabbit-0
export overcloud-rabbit-1=overcloud-rabbit-1
export overcloud-rabbit-2=overcloud-rabbit-2
=#=#=#= End test: List all nodes as bash exports - OK (0) =#=#=#=
* Passed: crmadmin - List all nodes as bash exports
=#=#=#= Begin test: List cluster nodes =#=#=#=
6
=#=#=#= End test: List cluster nodes - OK (0) =#=#=#=
* Passed: crmadmin - List cluster nodes
=#=#=#= Begin test: List guest nodes =#=#=#=
2
=#=#=#= End test: List guest nodes - OK (0) =#=#=#=
* Passed: crmadmin - List guest nodes
=#=#=#= Begin test: List remote nodes =#=#=#=
3
=#=#=#= End test: List remote nodes - OK (0) =#=#=#=
* Passed: crmadmin - List remote nodes
=#=#=#= Begin test: List cluster,remote nodes =#=#=#=
9
=#=#=#= End test: List cluster,remote nodes - OK (0) =#=#=#=
* Passed: crmadmin - List cluster,remote nodes
=#=#=#= Begin test: List guest,remote nodes =#=#=#=
5
=#=#=#= End test: List guest,remote nodes - OK (0) =#=#=#=
* Passed: crmadmin - List guest,remote nodes
=#=#=#= Begin test: Show allocation scores with crm_simulate =#=#=#=
=#=#=#= End test: Show allocation scores with crm_simulate - OK (0) =#=#=#=
* Passed: crm_simulate - Show allocation scores with crm_simulate
=#=#=#= Begin test: Show utilization with crm_simulate =#=#=#=
4 of 32 resource instances DISABLED and 0 BLOCKED from further action due to failure
[ cluster01 cluster02 ]
[ httpd-bundle-0 httpd-bundle-1 ]
Started: [ cluster01 cluster02 ]
Fencing (stonith:fence_xvm): Started cluster01
dummy (ocf:pacemaker:Dummy): Started cluster02
Stopped (disabled): [ cluster01 cluster02 ]
inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
Public-IP (ocf:heartbeat:IPaddr): Started cluster02
Email (lsb:exim): Started cluster02
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
Original: cluster01 capacity:
Original: cluster02 capacity:
Original: httpd-bundle-0 capacity:
Original: httpd-bundle-1 capacity:
Original: httpd-bundle-2 capacity:
pcmk__assign_resource: ping:0 utilization on cluster02:
pcmk__assign_resource: ping:1 utilization on cluster01:
pcmk__assign_resource: Fencing utilization on cluster01:
pcmk__assign_resource: dummy utilization on cluster02:
pcmk__assign_resource: httpd-bundle-docker-0 utilization on cluster01:
pcmk__assign_resource: httpd-bundle-docker-1 utilization on cluster02:
pcmk__assign_resource: httpd-bundle-ip-192.168.122.131 utilization on cluster01:
pcmk__assign_resource: httpd-bundle-0 utilization on cluster01:
pcmk__assign_resource: httpd:0 utilization on httpd-bundle-0:
pcmk__assign_resource: httpd-bundle-ip-192.168.122.132 utilization on cluster02:
pcmk__assign_resource: httpd-bundle-1 utilization on cluster02:
pcmk__assign_resource: httpd:1 utilization on httpd-bundle-1:
pcmk__assign_resource: httpd-bundle-2 utilization on cluster01:
pcmk__assign_resource: httpd:2 utilization on httpd-bundle-2:
pcmk__assign_resource: Public-IP utilization on cluster02:
pcmk__assign_resource: Email utilization on cluster02:
pcmk__assign_resource: mysql-proxy:0 utilization on cluster02:
pcmk__assign_resource: mysql-proxy:1 utilization on cluster01:
pcmk__assign_resource: promotable-rsc:0 utilization on cluster02:
pcmk__assign_resource: promotable-rsc:1 utilization on cluster01:
Remaining: cluster01 capacity:
Remaining: cluster02 capacity:
Remaining: httpd-bundle-0 capacity:
Remaining: httpd-bundle-1 capacity:
Remaining: httpd-bundle-2 capacity:
Start httpd-bundle-2 ( cluster01 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
Start httpd:2 ( httpd-bundle-2 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
=#=#=#= End test: Show utilization with crm_simulate - OK (0) =#=#=#=
* Passed: crm_simulate - Show utilization with crm_simulate
=#=#=#= Begin test: Simulate injecting a failure =#=#=#=
4 of 32 resource instances DISABLED and 0 BLOCKED from further action due to failure
Current cluster status:
* Node List:
* Online: [ cluster01 cluster02 ]
* GuestOnline: [ httpd-bundle-0 httpd-bundle-1 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Unpromoted: [ cluster01 ]
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:
* Recover ping:0 ( cluster02 )
* Start httpd-bundle-2 ( cluster01 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
* Start httpd:2 ( httpd-bundle-2 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
Executing Cluster Transition:
* Cluster action: clear_failcount for ping on cluster02
* Pseudo action: ping-clone_stop_0
* Pseudo action: httpd-bundle_start_0
* Resource action: ping stop on cluster02
* Pseudo action: ping-clone_stopped_0
* Pseudo action: ping-clone_start_0
* Pseudo action: httpd-bundle-clone_start_0
* Resource action: ping start on cluster02
* Resource action: ping monitor=10000 on cluster02
* Pseudo action: ping-clone_running_0
* Pseudo action: httpd-bundle-clone_running_0
* Pseudo action: httpd-bundle_running_0
Revised Cluster Status:
* Node List:
* Online: [ cluster01 cluster02 ]
* GuestOnline: [ httpd-bundle-0 httpd-bundle-1 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Unpromoted: [ cluster01 ]
=#=#=#= End test: Simulate injecting a failure - OK (0) =#=#=#=
* Passed: crm_simulate - Simulate injecting a failure
=#=#=#= Begin test: Simulate bringing a node down =#=#=#=
4 of 32 resource instances DISABLED and 0 BLOCKED from further action due to failure
Current cluster status:
* Node List:
* Online: [ cluster01 cluster02 ]
* GuestOnline: [ httpd-bundle-0 httpd-bundle-1 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Unpromoted: [ cluster01 ]
Performing Requested Modifications:
* Taking node cluster01 offline
Transition Summary:
* Fence (off) httpd-bundle-0 (resource: httpd-bundle-docker-0) 'guest is unclean'
* Start Fencing ( cluster02 )
* Start httpd-bundle-0 ( cluster02 ) due to unrunnable httpd-bundle-docker-0 start (blocked)
* Stop httpd:0 ( httpd-bundle-0 ) due to unrunnable httpd-bundle-docker-0 start
* Start httpd-bundle-2 ( cluster02 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
* Start httpd:2 ( httpd-bundle-2 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
Executing Cluster Transition:
* Resource action: Fencing start on cluster02
* Pseudo action: stonith-httpd-bundle-0-off on httpd-bundle-0
* Pseudo action: httpd-bundle_stop_0
* Pseudo action: httpd-bundle_start_0
* Resource action: Fencing monitor=60000 on cluster02
* Pseudo action: httpd-bundle-clone_stop_0
* Pseudo action: httpd_stop_0
* Pseudo action: httpd-bundle-clone_stopped_0
* Pseudo action: httpd-bundle-clone_start_0
* Pseudo action: httpd-bundle_stopped_0
* Pseudo action: httpd-bundle-clone_running_0
* Pseudo action: httpd-bundle_running_0
Revised Cluster Status:
* Node List:
* Online: [ cluster02 ]
* OFFLINE: [ cluster01 ]
* GuestOnline: [ httpd-bundle-1 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster02 ]
* Stopped: [ cluster01 ]
* Fencing (stonith:fence_xvm): Started cluster02
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): FAILED
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster02 ]
* Stopped: [ cluster01 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Stopped: [ cluster01 ]
=#=#=#= End test: Simulate bringing a node down - OK (0) =#=#=#=
* Passed: crm_simulate - Simulate bringing a node down
=#=#=#= Begin test: Simulate a node failing =#=#=#=
4 of 32 resource instances DISABLED and 0 BLOCKED from further action due to failure
Current cluster status:
* Node List:
* Online: [ cluster01 cluster02 ]
* GuestOnline: [ httpd-bundle-0 httpd-bundle-1 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Unpromoted: [ cluster01 ]
Performing Requested Modifications:
* Failing node cluster02
Transition Summary:
* Fence (off) httpd-bundle-1 (resource: httpd-bundle-docker-1) 'guest is unclean'
* Fence (reboot) cluster02 'peer is no longer part of the cluster'
* Stop ping:0 ( cluster02 ) due to node availability
* Stop dummy ( cluster02 ) due to node availability
* Stop httpd-bundle-ip-192.168.122.132 ( cluster02 ) due to node availability
* Stop httpd-bundle-docker-1 ( cluster02 ) due to node availability
* Stop httpd-bundle-1 ( cluster02 ) due to unrunnable httpd-bundle-docker-1 start
* Stop httpd:1 ( httpd-bundle-1 ) due to unrunnable httpd-bundle-docker-1 start
* Start httpd-bundle-2 ( cluster01 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
* Start httpd:2 ( httpd-bundle-2 ) due to unrunnable httpd-bundle-docker-2 start (blocked)
* Move Public-IP ( cluster02 -> cluster01 )
* Move Email ( cluster02 -> cluster01 )
* Stop mysql-proxy:0 ( cluster02 ) due to node availability
* Stop promotable-rsc:0 ( Promoted cluster02 ) due to node availability
Executing Cluster Transition:
* Pseudo action: httpd-bundle-1_stop_0
* Pseudo action: promotable-clone_demote_0
* Pseudo action: httpd-bundle_stop_0
* Pseudo action: httpd-bundle_start_0
* Fencing cluster02 (reboot)
* Pseudo action: ping-clone_stop_0
* Pseudo action: dummy_stop_0
* Pseudo action: httpd-bundle-docker-1_stop_0
* Pseudo action: exim-group_stop_0
* Pseudo action: Email_stop_0
* Pseudo action: mysql-clone-group_stop_0
* Pseudo action: promotable-rsc_demote_0
* Pseudo action: promotable-clone_demoted_0
* Pseudo action: promotable-clone_stop_0
* Pseudo action: stonith-httpd-bundle-1-off on httpd-bundle-1
* Pseudo action: ping_stop_0
* Pseudo action: ping-clone_stopped_0
* Pseudo action: httpd-bundle-clone_stop_0
* Pseudo action: httpd-bundle-ip-192.168.122.132_stop_0
* Pseudo action: Public-IP_stop_0
* Pseudo action: mysql-group:0_stop_0
* Pseudo action: mysql-proxy_stop_0
* Pseudo action: promotable-rsc_stop_0
* Pseudo action: promotable-clone_stopped_0
* Pseudo action: httpd_stop_0
* Pseudo action: httpd-bundle-clone_stopped_0
* Pseudo action: httpd-bundle-clone_start_0
* Pseudo action: exim-group_stopped_0
* Pseudo action: exim-group_start_0
* Resource action: Public-IP start on cluster01
* Resource action: Email start on cluster01
* Pseudo action: mysql-group:0_stopped_0
* Pseudo action: mysql-clone-group_stopped_0
* Pseudo action: httpd-bundle_stopped_0
* Pseudo action: httpd-bundle-clone_running_0
* Pseudo action: exim-group_running_0
* Pseudo action: httpd-bundle_running_0
Revised Cluster Status:
* Node List:
* Online: [ cluster01 ]
* OFFLINE: [ cluster02 ]
* GuestOnline: [ httpd-bundle-0 ]
* Full List of Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 ]
* Stopped: [ cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Stopped
* Clone Set: inactive-clone [inactive-dhcpd] (disabled):
* Stopped (disabled): [ cluster01 cluster02 ]
* Resource Group: inactive-group (disabled):
* inactive-dummy-1 (ocf:pacemaker:Dummy): Stopped (disabled)
* inactive-dummy-2 (ocf:pacemaker:Dummy): Stopped (disabled)
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): FAILED
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster01
* Email (lsb:exim): Started cluster01
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 ]
* Stopped: [ cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Unpromoted: [ cluster01 ]
* Stopped: [ cluster02 ]
=#=#=#= End test: Simulate a node failing - OK (0) =#=#=#=
* Passed: crm_simulate - Simulate a node failing
=#=#=#= Begin test: List a promotable clone resource =#=#=#=
resource promotable-clone is running on: cluster01
resource promotable-clone is running on: cluster02 Promoted
=#=#=#= End test: List a promotable clone resource - OK (0) =#=#=#=
* Passed: crm_resource - List a promotable clone resource
=#=#=#= Begin test: List the primitive of a promotable clone resource =#=#=#=
resource promotable-rsc is running on: cluster01
resource promotable-rsc is running on: cluster02 Promoted
=#=#=#= End test: List the primitive of a promotable clone resource - OK (0) =#=#=#=
* Passed: crm_resource - List the primitive of a promotable clone resource
=#=#=#= Begin test: List a single instance of a promotable clone resource =#=#=#=
resource promotable-rsc:0 is running on: cluster02 Promoted
=#=#=#= End test: List a single instance of a promotable clone resource - OK (0) =#=#=#=
* Passed: crm_resource - List a single instance of a promotable clone resource
=#=#=#= Begin test: List another instance of a promotable clone resource =#=#=#=
resource promotable-rsc:1 is running on: cluster01
=#=#=#= End test: List another instance of a promotable clone resource - OK (0) =#=#=#=
* Passed: crm_resource - List another instance of a promotable clone resource
=#=#=#= Begin test: List a promotable clone resource in XML =#=#=#=
cluster01cluster02
=#=#=#= End test: List a promotable clone resource in XML - OK (0) =#=#=#=
* Passed: crm_resource - List a promotable clone resource in XML
=#=#=#= Begin test: List the primitive of a promotable clone resource in XML =#=#=#=
cluster01cluster02
=#=#=#= End test: List the primitive of a promotable clone resource in XML - OK (0) =#=#=#=
* Passed: crm_resource - List the primitive of a promotable clone resource in XML
=#=#=#= Begin test: List a single instance of a promotable clone resource in XML =#=#=#=
cluster02
=#=#=#= End test: List a single instance of a promotable clone resource in XML - OK (0) =#=#=#=
* Passed: crm_resource - List a single instance of a promotable clone resource in XML
=#=#=#= Begin test: List another instance of a promotable clone resource in XML =#=#=#=
cluster01
=#=#=#= End test: List another instance of a promotable clone resource in XML - OK (0) =#=#=#=
* Passed: crm_resource - List another instance of a promotable clone resource in XML
=#=#=#= Begin test: Try to move an instance of a cloned resource =#=#=#=
crm_resource: Cannot operate on clone resource instance 'promotable-rsc:0'
Error performing operation: Invalid parameter
=#=#=#= End test: Try to move an instance of a cloned resource - Invalid parameter (2) =#=#=#=
* Passed: crm_resource - Try to move an instance of a cloned resource
=#=#=#= Begin test: Query a nonexistent promotable score attribute =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query a nonexistent promotable score attribute - No such object (105) =#=#=#=
* Passed: crm_attribute - Query a nonexistent promotable score attribute
=#=#=#= Begin test: Query a nonexistent promotable score attribute (XML) =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query a nonexistent promotable score attribute (XML) - No such object (105) =#=#=#=
* Passed: crm_attribute - Query a nonexistent promotable score attribute (XML)
=#=#=#= Begin test: Delete a nonexistent promotable score attribute =#=#=#=
=#=#=#= End test: Delete a nonexistent promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Delete a nonexistent promotable score attribute
=#=#=#= Begin test: Delete a nonexistent promotable score attribute (XML) =#=#=#=
=#=#=#= End test: Delete a nonexistent promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Delete a nonexistent promotable score attribute (XML)
=#=#=#= Begin test: Query after deleting a nonexistent promotable score attribute =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query after deleting a nonexistent promotable score attribute - No such object (105) =#=#=#=
* Passed: crm_attribute - Query after deleting a nonexistent promotable score attribute
=#=#=#= Begin test: Query after deleting a nonexistent promotable score attribute (XML) =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query after deleting a nonexistent promotable score attribute (XML) - No such object (105) =#=#=#=
* Passed: crm_attribute - Query after deleting a nonexistent promotable score attribute (XML)
=#=#=#= Begin test: Update a nonexistent promotable score attribute =#=#=#=
=#=#=#= End test: Update a nonexistent promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Update a nonexistent promotable score attribute
=#=#=#= Begin test: Update a nonexistent promotable score attribute (XML) =#=#=#=
=#=#=#= 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
=#=#=#= 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) =#=#=#=
=#=#=#= End test: Query after updating a nonexistent promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating a nonexistent promotable score attribute (XML)
=#=#=#= Begin test: Update an existing promotable score attribute =#=#=#=
=#=#=#= End test: Update an existing promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Update an existing promotable score attribute
=#=#=#= Begin test: Update an existing promotable score attribute (XML) =#=#=#=
=#=#=#= 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
=#=#=#= 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) =#=#=#=
=#=#=#= End test: Query after updating an existing promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Query after updating an existing promotable score attribute (XML)
=#=#=#= Begin test: Delete an existing promotable score attribute =#=#=#=
Deleted status attribute: id=status-1-master-promotable-rsc name=master-promotable-rsc
=#=#=#= End test: Delete an existing promotable score attribute - OK (0) =#=#=#=
* Passed: crm_attribute - Delete an existing promotable score attribute
=#=#=#= Begin test: Delete an existing promotable score attribute (XML) =#=#=#=
=#=#=#= End test: Delete an existing promotable score attribute (XML) - OK (0) =#=#=#=
* Passed: crm_attribute - Delete an existing promotable score attribute (XML)
=#=#=#= Begin test: Query after deleting an existing promotable score attribute =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query after deleting an existing promotable score attribute - No such object (105) =#=#=#=
* Passed: crm_attribute - Query after deleting an existing promotable score attribute
=#=#=#= Begin test: Query after deleting an existing promotable score attribute (XML) =#=#=#=
crm_attribute: Error performing operation: No such device or address
=#=#=#= End test: Query after deleting an existing promotable score attribute (XML) - No such object (105) =#=#=#=
* Passed: crm_attribute - Query after deleting an existing promotable score attribute (XML)
=#=#=#= Begin test: Update a promotable score attribute to -INFINITY =#=#=#=
=#=#=#= End test: Update a promotable score attribute to -INFINITY - OK (0) =#=#=#=
* Passed: crm_attribute - Update a promotable score attribute to -INFINITY
=#=#=#= Begin test: Update a promotable score attribute to -INFINITY (XML) =#=#=#=
=#=#=#= 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
=#=#=#= 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) =#=#=#=
=#=#=#= 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
=#=#=#= 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 =#=#=#=
crm_attribute: -p/--promotion must be called from an OCF resource agent or with a resource ID specified
=#=#=#= End test: Return usage error if both -p and OCF_RESOURCE_INSTANCE are empty strings - Incorrect usage (64) =#=#=#=
* Passed: crm_attribute - Return usage error if both -p and OCF_RESOURCE_INSTANCE are empty strings
=#=#=#= Begin test: Check that CIB_file="-" works - crm_mon =#=#=#=
Cluster Summary:
* Stack: corosync
* Current DC: cluster02 (version) - partition with quorum
* Last updated:
* Last change:
* 5 nodes configured
* 32 resource instances configured (4 DISABLED)
Node List:
* Online: [ cluster01 cluster02 ]
* GuestOnline: [ httpd-bundle-0 httpd-bundle-1 ]
Active Resources:
* Clone Set: ping-clone [ping]:
* Started: [ cluster01 cluster02 ]
* Fencing (stonith:fence_xvm): Started cluster01
* dummy (ocf:pacemaker:Dummy): Started cluster02
* Container bundle set: httpd-bundle [pcmk:http]:
* httpd-bundle-0 (192.168.122.131) (ocf:heartbeat:apache): Started cluster01
* httpd-bundle-1 (192.168.122.132) (ocf:heartbeat:apache): Started cluster02
* httpd-bundle-2 (192.168.122.133) (ocf:heartbeat:apache): Stopped
* Resource Group: exim-group:
* Public-IP (ocf:heartbeat:IPaddr): Started cluster02
* Email (lsb:exim): Started cluster02
* Clone Set: mysql-clone-group [mysql-group]:
* Started: [ cluster01 cluster02 ]
* Clone Set: promotable-clone [promotable-rsc] (promotable):
* Promoted: [ cluster02 ]
* Unpromoted: [ cluster01 ]
=#=#=#= End test: Check that CIB_file="-" works - crm_mon - OK (0) =#=#=#=
* Passed: cat - Check that CIB_file="-" works - crm_mon
=#=#=#= Begin test: Check that CIB_file="-" works - crm_resource =#=#=#=
=#=#=#= End test: Check that CIB_file="-" works - crm_resource - OK (0) =#=#=#=
* Passed: cat - Check that CIB_file="-" works - crm_resource
=#=#=#= Begin test: Check that CIB_file="-" works - crmadmin =#=#=#=
11
=#=#=#= End test: Check that CIB_file="-" works - crmadmin - OK (0) =#=#=#=
* Passed: cat - Check that CIB_file="-" works - crmadmin
=#=#=#= Begin test: Get active shadow instance (no active instance) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance (no active instance) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance (no active instance)
=#=#=#= Begin test: Get active shadow instance (no active instance) (XML) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance (no active instance) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance (no active instance) (XML)
=#=#=#= Begin test: Get active shadow instance's file name (no active instance) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's file name (no active instance) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's file name (no active instance)
=#=#=#= Begin test: Get active shadow instance's file name (no active instance) (XML) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's file name (no active instance) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's file name (no active instance) (XML)
=#=#=#= Begin test: Get active shadow instance's contents (no active instance) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's contents (no active instance) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (no active instance)
=#=#=#= Begin test: Get active shadow instance's contents (no active instance) (XML) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's contents (no active instance) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (no active instance) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (no active instance) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's diff (no active instance) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (no active instance)
=#=#=#= Begin test: Get active shadow instance's diff (no active instance) (XML) =#=#=#=
crm_shadow: No active shadow configuration defined
=#=#=#= End test: Get active shadow instance's diff (no active instance) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (no active instance) (XML)
=#=#=#= Begin test: Create copied shadow instance =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance
=#=#=#= Begin test: Create copied shadow instance (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (XML)
=#=#=#= Begin test: Get active shadow instance (copied) =#=#=#=
cts-cli
=#=#=#= End test: Get active shadow instance (copied) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance (copied)
=#=#=#= Begin test: Get active shadow instance (copied) (XML) =#=#=#=
=#=#=#= End test: Get active shadow instance (copied) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance (copied) (XML)
=#=#=#= Begin test: Get active shadow instance's file name (copied) =#=#=#=
/tmp/cts-cli.shadow/shadow.cts-cli
=#=#=#= End test: Get active shadow instance's file name (copied) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's file name (copied)
=#=#=#= Begin test: Get active shadow instance's file name (copied) (XML) =#=#=#=
=#=#=#= End test: Get active shadow instance's file name (copied) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's file name (copied) (XML)
=#=#=#= Begin test: Get active shadow instance's contents (copied) =#=#=#=
=#=#=#= End test: Get active shadow instance's contents (copied) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (copied)
=#=#=#= Begin test: Get active shadow instance's contents (copied) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's contents (copied) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (copied) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (copied) =#=#=#=
=#=#=#= End test: Get active shadow instance's diff (copied) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (copied)
=#=#=#= Begin test: Get active shadow instance's diff (copied) (XML) =#=#=#=
=#=#=#= End test: Get active shadow instance's diff (copied) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (copied) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (after changes) =#=#=#=
Diff: --- 1.1.173 2
Diff: +++ 1.4.1 (null)
-- /cib/configuration/op_defaults
+ /cib: @epoch=4, @num_updates=1
+ /cib/configuration/resources/primitive[@id='dummy']: @description=desc
++ /cib/configuration/resources:
++ /cib/status:
=#=#=#= End test: Get active shadow instance's diff (after changes) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after changes)
=#=#=#= Begin test: Get active shadow instance's diff (after changes) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's diff (after changes) (XML) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after changes) (XML)
=#=#=#= Begin test: Commit shadow instance =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance
=#=#=#= Begin test: Commit shadow instance (force) =#=#=#=
=#=#=#= End test: Commit shadow instance (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (force)
=#=#=#= Begin test: Get active shadow instance's diff (after commit) =#=#=#=
Diff: --- 1.2.0 2
Diff: +++ 1.4.1 (null)
+ /cib: @epoch=4, @num_updates=1
++ /cib/status:
=#=#=#= End test: Get active shadow instance's diff (after commit) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after commit)
=#=#=#= Begin test: Commit shadow instance (force) (all) =#=#=#=
=#=#=#= End test: Commit shadow instance (force) (all) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (force) (all)
=#=#=#= Begin test: Get active shadow instance's diff (after commit all) =#=#=#=
Diff: --- 1.4.2 2
Diff: +++ 1.4.1 (null)
+ /cib: @num_updates=1
=#=#=#= End test: Get active shadow instance's diff (after commit all) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after commit all)
=#=#=#= Begin test: Commit shadow instance (XML) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (XML)
=#=#=#= Begin test: Commit shadow instance (force) (XML) =#=#=#=
=#=#=#= End test: Commit shadow instance (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (force) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (after commit) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's diff (after commit) (XML) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after commit) (XML)
=#=#=#= Begin test: Commit shadow instance (force) (all) (XML) =#=#=#=
=#=#=#= End test: Commit shadow instance (force) (all) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (force) (all) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (after commit all) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's diff (after commit all) (XML) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after commit all) (XML)
=#=#=#= Begin test: Commit shadow instance (no active instance) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (no active instance) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (no active instance)
=#=#=#= Begin test: Commit shadow instance (no active instance) (force) =#=#=#=
=#=#=#= End test: Commit shadow instance (no active instance) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (no active instance) (force)
=#=#=#= Begin test: Commit shadow instance (no active instance) (XML) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (no active instance) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (no active instance) (XML)
=#=#=#= Begin test: Commit shadow instance (no active instance) (force) (XML) =#=#=#=
=#=#=#= End test: Commit shadow instance (no active instance) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (no active instance) (force) (XML)
=#=#=#= Begin test: Commit shadow instance (mismatch) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
Additionally, the supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (mismatch) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (mismatch)
=#=#=#= Begin test: Commit shadow instance (mismatch) (force) =#=#=#=
=#=#=#= End test: Commit shadow instance (mismatch) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (mismatch) (force)
=#=#=#= Begin test: Commit shadow instance (mismatch) (XML) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
Additionally, the supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (mismatch) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (mismatch) (XML)
=#=#=#= Begin test: Commit shadow instance (mismatch) (force) (XML) =#=#=#=
=#=#=#= End test: Commit shadow instance (mismatch) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (mismatch) (force) (XML)
=#=#=#= Begin test: Commit shadow instance (nonexistent shadow file) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (nonexistent shadow file) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent shadow file)
=#=#=#= Begin test: Commit shadow instance (nonexistent shadow file) (force) =#=#=#=
crm_shadow: Could not access shadow instance 'nonexistent_shadow': No such file or directory
=#=#=#= End test: Commit shadow instance (nonexistent shadow file) (force) - No such object (105) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent shadow file) (force)
=#=#=#= Begin test: Get active shadow instance's diff (nonexistent shadow file) =#=#=#=
crm_shadow: Could not access shadow instance 'nonexistent_shadow': No such file or directory
=#=#=#= End test: Get active shadow instance's diff (nonexistent shadow file) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (nonexistent shadow file)
=#=#=#= Begin test: Commit shadow instance (nonexistent shadow file) (XML) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (nonexistent shadow file) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent shadow file) (XML)
=#=#=#= Begin test: Commit shadow instance (nonexistent shadow file) (force) (XML) =#=#=#=
crm_shadow: Could not access shadow instance 'nonexistent_shadow': No such file or directory
=#=#=#= End test: Commit shadow instance (nonexistent shadow file) (force) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent shadow file) (force) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (nonexistent shadow file) (XML) =#=#=#=
crm_shadow: Could not access shadow instance 'nonexistent_shadow': No such file or directory
=#=#=#= End test: Get active shadow instance's diff (nonexistent shadow file) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (nonexistent shadow file) (XML)
=#=#=#= Begin test: Commit shadow instance (nonexistent CIB file) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (nonexistent CIB file) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent CIB file)
=#=#=#= Begin test: Commit shadow instance (nonexistent CIB file) (force) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Commit shadow instance (nonexistent CIB file) (force) - No such object (105) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent CIB file) (force)
=#=#=#= Begin test: Get active shadow instance's diff (nonexistent CIB file) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Get active shadow instance's diff (nonexistent CIB file) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (nonexistent CIB file)
=#=#=#= Begin test: Commit shadow instance (nonexistent CIB file) (XML) =#=#=#=
crm_shadow: The commit command overwrites the active cluster configuration.
To prevent accidental destruction of the cluster, the --force flag is required in order to proceed.
=#=#=#= End test: Commit shadow instance (nonexistent CIB file) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent CIB file) (XML)
=#=#=#= Begin test: Commit shadow instance (nonexistent CIB file) (force) (XML) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Commit shadow instance (nonexistent CIB file) (force) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Commit shadow instance (nonexistent CIB file) (force) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (nonexistent CIB file) (XML) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Get active shadow instance's diff (nonexistent CIB file) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (nonexistent CIB file) (XML)
=#=#=#= Begin test: Delete shadow instance =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance
=#=#=#= Begin test: Delete shadow instance (force) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (force)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (XML) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (XML)
=#=#=#= Begin test: Delete shadow instance (force) (XML) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (force) (XML)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (no active instance) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (no active instance) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (no active instance)
=#=#=#= Begin test: Delete shadow instance (no active instance) (force) =#=#=#=
=#=#=#= End test: Delete shadow instance (no active instance) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (no active instance) (force)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (no active instance) (XML) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (no active instance) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (no active instance) (XML)
=#=#=#= Begin test: Delete shadow instance (no active instance) (force) (XML) =#=#=#=
=#=#=#= End test: Delete shadow instance (no active instance) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (no active instance) (force) (XML)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (mismatch) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
Additionally, the supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (mismatch) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (mismatch)
=#=#=#= Begin test: Delete shadow instance (mismatch) (force) =#=#=#=
=#=#=#= End test: Delete shadow instance (mismatch) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (mismatch) (force)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (mismatch) (XML) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
Additionally, the supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (mismatch) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (mismatch) (XML)
=#=#=#= Begin test: Delete shadow instance (mismatch) (force) (XML) =#=#=#=
=#=#=#= End test: Delete shadow instance (mismatch) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (mismatch) (force) (XML)
=#=#=#= Begin test: Delete shadow instance (nonexistent shadow file) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (nonexistent shadow file) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent shadow file)
=#=#=#= Begin test: Delete shadow instance (nonexistent shadow file) (force) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (nonexistent shadow file) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent shadow file) (force)
=#=#=#= Begin test: Delete shadow instance (nonexistent shadow file) (XML) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (nonexistent shadow file) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent shadow file) (XML)
=#=#=#= Begin test: Delete shadow instance (nonexistent shadow file) (force) (XML) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (nonexistent shadow file) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent shadow file) (force) (XML)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (nonexistent CIB file) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (nonexistent CIB file) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent CIB file)
=#=#=#= Begin test: Delete shadow instance (nonexistent CIB file) (force) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (nonexistent CIB file) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent CIB file) (force)
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= Begin test: Delete shadow instance (nonexistent CIB file) (XML) =#=#=#=
crm_shadow: The delete command removes the specified shadow file.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Delete shadow instance (nonexistent CIB file) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent CIB file) (XML)
=#=#=#= Begin test: Delete shadow instance (nonexistent CIB file) (force) (XML) =#=#=#=
Remember to unset the CIB_shadow variable by entering the following into your shell:
unset CIB_shadow
=#=#=#= End test: Delete shadow instance (nonexistent CIB file) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Delete shadow instance (nonexistent CIB file) (force) (XML)
=#=#=#= Begin test: Create copied shadow instance (no active instance) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (no active instance) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (no active instance)
=#=#=#= Begin test: Create copied shadow instance (no active instance) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (no active instance) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (no active instance) (XML)
=#=#=#= Begin test: Create copied shadow instance (mismatch) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (mismatch) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (mismatch)
=#=#=#= Begin test: Create copied shadow instance (mismatch) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (mismatch) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (mismatch) (XML)
=#=#=#= Begin test: Create copied shadow instance (file already exists) =#=#=#=
crm_shadow: A shadow instance 'cts-cli' already exists.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Create copied shadow instance (file already exists) - Cannot create output file (73) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (file already exists)
=#=#=#= Begin test: Create copied shadow instance (file already exists) (force) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (file already exists) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (file already exists) (force)
=#=#=#= Begin test: Create copied shadow instance (file already exists) (XML) =#=#=#=
crm_shadow: A shadow instance 'cts-cli' already exists.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Create copied shadow instance (file already exists) (XML) - Cannot create output file (73) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (file already exists) (XML)
=#=#=#= Begin test: Create copied shadow instance (file already exists) (force) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create copied shadow instance (file already exists) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (file already exists) (force) (XML)
=#=#=#= Begin test: Create copied shadow instance (nonexistent CIB file) (force) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Create copied shadow instance (nonexistent CIB file) (force) - No such object (105) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (nonexistent CIB file) (force)
=#=#=#= Begin test: Create copied shadow instance (nonexistent CIB file) (force) (XML) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Create copied shadow instance (nonexistent CIB file) (force) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Create copied shadow instance (nonexistent CIB file) (force) (XML)
=#=#=#= Begin test: Create empty shadow instance =#=#=#=
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
=#=#=#= End test: Create empty shadow instance - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance
=#=#=#= Begin test: Create empty shadow instance (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create empty shadow instance (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (XML)
=#=#=#= Begin test: Create empty shadow instance (no active instance) =#=#=#=
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
=#=#=#= End test: Create empty shadow instance (no active instance) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (no active instance)
=#=#=#= Begin test: Create empty shadow instance (no active instance) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create empty shadow instance (no active instance) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (no active instance) (XML)
=#=#=#= Begin test: Create empty shadow instance (mismatch) =#=#=#=
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
=#=#=#= End test: Create empty shadow instance (mismatch) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (mismatch)
=#=#=#= Begin test: Create empty shadow instance (mismatch) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create empty shadow instance (mismatch) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (mismatch) (XML)
=#=#=#= Begin test: Create empty shadow instance (nonexistent CIB file) =#=#=#=
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
=#=#=#= End test: Create empty shadow instance (nonexistent CIB file) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (nonexistent CIB file)
=#=#=#= Begin test: Create empty shadow instance (nonexistent CIB file) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create empty shadow instance (nonexistent CIB file) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (nonexistent CIB file) (XML)
=#=#=#= Begin test: Create empty shadow instance (file already exists) =#=#=#=
crm_shadow: A shadow instance 'cts-cli' already exists.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Create empty shadow instance (file already exists) - Cannot create output file (73) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (file already exists)
=#=#=#= Begin test: Create empty shadow instance (file already exists) (force) =#=#=#=
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
=#=#=#= End test: Create empty shadow instance (file already exists) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (file already exists) (force)
=#=#=#= Begin test: Create empty shadow instance (file already exists) (XML) =#=#=#=
crm_shadow: A shadow instance 'cts-cli' already exists.
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Create empty shadow instance (file already exists) (XML) - Cannot create output file (73) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (file already exists) (XML)
=#=#=#= Begin test: Create empty shadow instance (file already exists) (force) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Create empty shadow instance (file already exists) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Create empty shadow instance (file already exists) (force) (XML)
=#=#=#= Begin test: Get active shadow instance's contents (empty CIB) =#=#=#=
=#=#=#= End test: Get active shadow instance's contents (empty CIB) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (empty CIB)
=#=#=#= Begin test: Get active shadow instance's contents (empty CIB) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's contents (empty CIB) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's contents (empty CIB) (XML)
=#=#=#= Begin test: Get active shadow instance's diff (empty CIB) =#=#=#=
Diff: --- 1.1.173 2
Diff: +++ 0.1.0 (null)
-- /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']
-- /cib/configuration/nodes/node[@id='1']
-- /cib/configuration/nodes/node[@id='2']
-- /cib/configuration/resources/clone[@id='ping-clone']
-- /cib/configuration/resources/primitive[@id='Fencing']
-- /cib/configuration/resources/primitive[@id='dummy']
-- /cib/configuration/resources/clone[@id='inactive-clone']
-- /cib/configuration/resources/group[@id='inactive-group']
-- /cib/configuration/resources/bundle[@id='httpd-bundle']
-- /cib/configuration/resources/group[@id='exim-group']
-- /cib/configuration/resources/clone[@id='mysql-clone-group']
-- /cib/configuration/resources/clone[@id='promotable-clone']
-- /cib/configuration/constraints/rsc_location[@id='not-on-cluster1']
-- /cib/configuration/constraints/rsc_location[@id='loc-promotable-clone']
-- /cib/configuration/tags
-- /cib/configuration/op_defaults
-- /cib/status/node_state[@id='2']
-- /cib/status/node_state[@id='1']
-- /cib/status/node_state[@id='httpd-bundle-0']
-- /cib/status/node_state[@id='httpd-bundle-1']
+ /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)
=#=#=#= Begin test: Get active shadow instance's diff (empty CIB) (XML) =#=#=#=
]]>
=#=#=#= End test: Get active shadow instance's diff (empty CIB) (XML) - Error occurred (1) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (empty CIB) (XML)
=#=#=#= Begin test: Reset shadow instance =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance
=#=#=#= Begin test: Get active shadow instance's diff (after reset) =#=#=#=
=#=#=#= End test: Get active shadow instance's diff (after reset) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after reset)
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: Reset shadow instance (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (XML)
=#=#=#= Begin test: Get active shadow instance's diff (after reset) (XML) =#=#=#=
=#=#=#= End test: Get active shadow instance's diff (after reset) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Get active shadow instance's diff (after reset) (XML)
=#=#=#= Begin test: Reset shadow instance (no active instance) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (no active instance) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (no active instance)
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: Reset shadow instance (no active instance) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (no active instance) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (no active instance) (XML)
=#=#=#= Begin test: Reset shadow instance (mismatch) =#=#=#=
crm_shadow: The supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Reset shadow instance (mismatch) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (mismatch)
=#=#=#= Begin test: Reset shadow instance (mismatch) (force) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (mismatch) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (mismatch) (force)
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: Reset shadow instance (mismatch) (XML) =#=#=#=
crm_shadow: The supplied shadow instance (cts-cli) is not the same as the active one (nonexistent_shadow).
To prevent accidental destruction of the shadow file, the --force flag is required in order to proceed.
=#=#=#= End test: Reset shadow instance (mismatch) (XML) - Incorrect usage (64) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (mismatch) (XML)
=#=#=#= Begin test: Reset shadow instance (mismatch) (force) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (mismatch) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (mismatch) (force) (XML)
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: Reset shadow instance (nonexistent CIB file) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Reset shadow instance (nonexistent CIB file) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent CIB file)
=#=#=#= Begin test: Reset shadow instance (nonexistent CIB file) (XML) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Reset shadow instance (nonexistent CIB file) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent CIB file) (XML)
=#=#=#= Begin test: Reset shadow instance (nonexistent CIB file) (force) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Reset shadow instance (nonexistent CIB file) (force) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent CIB file) (force)
=#=#=#= Begin test: Reset shadow instance (nonexistent CIB file) (force) (XML) =#=#=#=
crm_shadow: Could not connect to CIB: No such device or address
=#=#=#= End test: Reset shadow instance (nonexistent CIB file) (force) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent CIB file) (force) (XML)
=#=#=#= Begin test: Reset shadow instance (nonexistent shadow file) =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= End test: Reset shadow instance (nonexistent shadow file) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent shadow file)
=#=#=#= Begin test: Reset shadow instance (nonexistent shadow file) (force) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (nonexistent shadow file) (force) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent shadow file) (force)
=#=#=#= Begin test: Reset shadow instance (nonexistent shadow file) (XML) =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= End test: Reset shadow instance (nonexistent shadow file) (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent shadow file) (XML)
=#=#=#= Begin test: Reset shadow instance (nonexistent shadow file) (force) (XML) =#=#=#=
A new shadow instance was created. To begin using it, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Reset shadow instance (nonexistent shadow file) (force) (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Reset shadow instance (nonexistent shadow file) (force) (XML)
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: Switch to new shadow instance =#=#=#=
To switch to the named shadow instance, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Switch to new shadow instance - OK (0) =#=#=#=
* Passed: crm_shadow - Switch to new shadow instance
=#=#=#= Begin test: Switch to new shadow instance (XML) =#=#=#=
To switch to the named shadow instance, enter the following into your shell:
export CIB_shadow=cts-cli
=#=#=#= End test: Switch to new shadow instance (XML) - OK (0) =#=#=#=
* Passed: crm_shadow - Switch to new shadow instance (XML)
=#=#=#= Begin test: Switch to nonexistent shadow instance =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= End test: Switch to nonexistent shadow instance - No such object (105) =#=#=#=
* Passed: crm_shadow - Switch to nonexistent shadow instance
=#=#=#= Begin test: Switch to nonexistent shadow instance (force) =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= End test: Switch to nonexistent shadow instance (force) - No such object (105) =#=#=#=
* Passed: crm_shadow - Switch to nonexistent shadow instance (force)
=#=#=#= Begin test: Switch to nonexistent shadow instance (XML) =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= End test: Switch to nonexistent shadow instance (XML) - No such object (105) =#=#=#=
* Passed: crm_shadow - Switch to nonexistent shadow instance (XML)
=#=#=#= Begin test: Switch to nonexistent shadow instance (force) (XML) =#=#=#=
crm_shadow: Could not access shadow instance 'cts-cli': No such file or directory
=#=#=#= 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 =#=#=#=
Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resourceIgnoring <clone> resource 'test2-clone' because configuration is invalidCIB did not pass schema validationErrors found during check: config not valid
=#=#=#= 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 =#=#=#=
Resource start-up disabled since no STONITH resources have been definedEither configure some or disable STONITH with the stonith-enabled optionNOTE: Clusters with shared data need STONITH to ensure data integrityNode pcmk-1 is unclean but cannot be fencedNode pcmk-2 is unclean but cannot be fencedCIB did not pass schema validationErrors found during check: config not valid
=#=#=#= 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
Resource test2:0 is of type systemd and therefore cannot be used as a promotable clone resourceIgnoring <clone> resource 'test2-clone' because configuration is invalidCIB did not pass schema validationErrors found during check: config not valid
=#=#=#= 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 warning: Fencing and resource management disabled due to lack of quorum
Resource start-up disabled since no STONITH resources have been definedEither configure some or disable STONITH with the stonith-enabled optionNOTE: Clusters with shared data need STONITH to ensure data integrityNode pcmk-1 is unclean but cannot be fencedNode pcmk-2 is unclean but cannot be fencedCIB did not pass schema validationErrors found during check: config not valid
=#=#=#= 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
=#=#=#= Begin test: Verify a file-specified valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verify a file-specified valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: crm_verify - Verify a file-specified valid configuration, outputting as xml
=#=#=#= Begin test: Verify a piped-in valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verify a piped-in valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: cat - Verify a piped-in valid configuration, outputting as xml
=#=#=#= Begin test: Verbosely verify a file-specified valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verbosely verify a file-specified valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: crm_verify - Verbosely verify a file-specified valid configuration, outputting as xml
=#=#=#= Begin test: Verbosely verify a piped-in valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verbosely verify a piped-in valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: cat - Verbosely verify a piped-in valid configuration, outputting as xml
=#=#=#= Begin test: Verify a string-supplied valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verify a string-supplied valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: crm_verify - Verify a string-supplied valid configuration, outputting as xml
=#=#=#= Begin test: Verbosely verify a string-supplied valid configuration, outputting as xml =#=#=#=
=#=#=#= End test: Verbosely verify a string-supplied valid configuration, outputting as xml - OK (0) =#=#=#=
* Passed: crm_verify - Verbosely verify a string-supplied valid configuration, outputting as xml
diff --git a/include/crm/common/xml.h b/include/crm/common/xml.h
index b00bf8d46a..b4174da280 100644
--- a/include/crm/common/xml.h
+++ b/include/crm/common/xml.h
@@ -1,195 +1,194 @@
/*
* 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__H
# define PCMK__CRM_COMMON_XML__H
# include
# include
# include
# include
# include
# include
# include
# include
# include
# include
# include
# include
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief Wrappers for and extensions to libxml2
* \ingroup core
*/
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);
/*
* Searching & Modifying
*/
xmlNode *get_xpath_object(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) {
// Can't use PCMK__XE_PROMOTABLE_LEGACY because it's internal
return PCMK_XE_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);
/*!
* \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 *sorted_xml(xmlNode * input, xmlNode * parent, gboolean recursive);
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path);
void crm_foreach_xpath_result(xmlNode *xml, const char *xpath,
void (*helper)(xmlNode*, void*), void *user_data);
xmlNode *expand_idref(xmlNode * input, xmlNode * top);
void freeXpathObject(xmlXPathObjectPtr xpathObj);
xmlNode *getXpathResult(xmlXPathObjectPtr xpathObj, int index);
void dedupXpathResults(xmlXPathObjectPtr xpathObj);
static inline int numXpathResults(xmlXPathObjectPtr xpathObj)
{
if(xpathObj == NULL || xpathObj->nodesetval == NULL) {
return 0;
}
return xpathObj->nodesetval->nodeNr;
}
bool xml_tracking_changes(xmlNode * xml);
bool xml_document_dirty(xmlNode *xml);
void xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls);
void xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml);
void xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml);
void xml_accept_changes(xmlNode * xml);
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3]);
xmlNode *xml_create_patchset(
int format, xmlNode *source, xmlNode *target, bool *config, bool manage_version);
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 crm_xml_sanitize_id(char *id);
void crm_xml_set_id(xmlNode *xml, const char *format, ...) G_GNUC_PRINTF(2, 3);
#if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
#include
#endif
#ifdef __cplusplus
}
#endif
#endif
diff --git a/include/crm/common/xml_compat.h b/include/crm/common/xml_compat.h
index 1a9cdbbb31..e6dbd60968 100644
--- a/include/crm/common/xml_compat.h
+++ b/include/crm/common/xml_compat.h
@@ -1,143 +1,146 @@
/*
* 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_COMPAT__H
# define PCMK__CRM_COMMON_XML_COMPAT__H
#include // gboolean
#include // xmlNode
#include // crm_xml_add()
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief Deprecated Pacemaker XML 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 (will be removed in a future release)
#define XML_PARANOIA_CHECKS 0
//! \deprecated This function will be removed in a future release
xmlDoc *getDocPtr(xmlNode *node);
//! \deprecated This function will be removed in a future release
int add_node_nocopy(xmlNode * parent, const char *name, xmlNode * child);
//! \deprecated This function will be removed in a future release
xmlNode *find_entity(xmlNode *parent, const char *node_name, const char *id);
//! \deprecated This function will be removed in a future release
char *xml_get_path(const xmlNode *xml);
//! \deprecated This function will be removed in a future release
void xml_log_changes(uint8_t level, const char *function, const xmlNode *xml);
//! \deprecated This function will be removed in a future release
void xml_log_patchset(uint8_t level, const char *function, const xmlNode *xml);
//! \deprecated Use xml_apply_patchset() instead
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml);
//! \deprecated Do not use (will be removed in a future release)
void crm_destroy_xml(gpointer data);
//! \deprecated Check children member directly
gboolean xml_has_children(const xmlNode *root);
//! \deprecated Use crm_xml_add() with "true" or "false" instead
static inline const char *
crm_xml_add_boolean(xmlNode *node, const char *name, gboolean value)
{
return crm_xml_add(node, name, (value? "true" : "false"));
}
//! \deprecated Use name member directly
static inline const char *
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);
//! \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);
+
#ifdef __cplusplus
}
#endif
#endif // PCMK__CRM_COMMON_XML_COMPAT__H
diff --git a/lib/cib/cib_utils.c b/lib/cib/cib_utils.c
index af7c1ee41b..8a93175fe2 100644
--- a/lib/cib/cib_utils.c
+++ b/lib/cib/cib_utils.c
@@ -1,1097 +1,1096 @@
/*
* Original copyright 2004 International Business Machines
* Later changes copyright 2008-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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
gboolean
cib_version_details(xmlNode * cib, int *admin_epoch, int *epoch, int *updates)
{
*epoch = -1;
*updates = -1;
*admin_epoch = -1;
if (cib == NULL) {
return FALSE;
} else {
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;
}
gboolean
cib_diff_version_details(xmlNode * diff, int *admin_epoch, int *epoch, int *updates,
int *_admin_epoch, int *_epoch, int *_updates)
{
int add[] = { 0, 0, 0 };
int del[] = { 0, 0, 0 };
xml_patch_versions(diff, add, del);
*admin_epoch = add[0];
*epoch = add[1];
*updates = add[2];
*_admin_epoch = del[0];
*_epoch = del[1];
*_updates = del[2];
return TRUE;
}
/*!
* \internal
* \brief Get the XML patchset from a CIB diff notification
*
* \param[in] msg CIB diff notification
* \param[out] patchset Where to store XML patchset
*
* \return Standard Pacemaker return code
*/
int
cib__get_notify_patchset(const xmlNode *msg, const xmlNode **patchset)
{
int rc = pcmk_err_generic;
CRM_ASSERT(patchset != NULL);
*patchset = NULL;
if (msg == NULL) {
crm_err("CIB diff notification received with no XML");
return ENOMSG;
}
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, PCMK__XA_CIB_UPDATE_RESULT);
if (*patchset == NULL) {
crm_err("CIB diff notification received with no patchset");
return ENOMSG;
}
return pcmk_rc_ok;
}
#define XPATH_DIFF_V1 "//" PCMK__XA_CIB_UPDATE_RESULT "//" PCMK__XE_DIFF_ADDED
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset (v1)
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* to \c PCMK_XE_CIB)
*
* \return \c true if \p element was modified, or \c false otherwise
*/
static bool
element_in_patchset_v1(const xmlNode *patchset, const char *element)
{
char *xpath = crm_strdup_printf(XPATH_DIFF_V1 "//%s",
pcmk__s(element, PCMK_XE_CIB));
xmlXPathObject *xpath_obj = xpath_search(patchset, xpath);
free(xpath);
if (xpath_obj == NULL) {
return false;
}
freeXpathObject(xpath_obj);
return true;
}
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset (v2)
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* 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
*/
static bool
element_in_patchset_v2(const xmlNode *patchset, const char *element)
{
const char *element_xpath = pcmk__cib_abs_xpath_for(element);
const char *parent_xpath = pcmk_cib_parent_name_for(element);
char *element_regex = NULL;
bool rc = false;
CRM_CHECK(element_xpath != NULL, return false); // Unsupported 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 = 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, 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
rc = true;
break;
}
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__xe_first_child(change, NULL, NULL, NULL),
element)) {
// Newly added element
rc = true;
break;
}
}
free(element_regex);
return rc;
}
/*!
* \internal
* \brief Check whether a given CIB element was modified in a CIB patchset
*
* \param[in] patchset CIB XML patchset
* \param[in] element XML tag of CIB element to check (\c NULL is equivalent
* 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
*/
bool
cib__element_in_patchset(const xmlNode *patchset, const char *element)
{
int format = 1;
CRM_ASSERT(patchset != NULL);
crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
return element_in_patchset_v1(patchset, element);
case 2:
return element_in_patchset_v2(patchset, element);
default:
crm_warn("Unknown patch format: %d", format);
return false;
}
}
/*!
* \brief Create XML for a new (empty) CIB
*
* \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().
*/
xmlNode *
createEmptyCib(int cib_epoch)
{
xmlNode *cib_root = NULL, *config = NULL;
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, xml_latest_schema());
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 = pcmk__xe_create(cib_root, PCMK_XE_CONFIGURATION);
pcmk__xe_create(cib_root, PCMK_XE_STATUS);
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 = 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
return cib_root;
}
static bool
cib_acl_enabled(xmlNode *xml, const char *user)
{
bool rc = FALSE;
if(pcmk_acl_required(user)) {
const char *value = NULL;
GHashTable *options = pcmk__strkey_table(free, free);
cib_read_config(options, xml);
value = pcmk__cluster_option(options, PCMK_OPT_ENABLE_ACL);
rc = crm_is_true(value);
g_hash_table_destroy(options);
}
crm_trace("CIB ACL is %s", rc ? "enabled" : "disabled");
return rc;
}
/*!
* \internal
* \brief Determine whether to perform operations on a scratch copy of the CIB
*
* \param[in] op CIB operation
* \param[in] section CIB section
* \param[in] call_options CIB call options
*
* \return \p true if we should make a copy of the CIB, or \p false otherwise
*/
static bool
should_copy_cib(const char *op, const char *section, int call_options)
{
if (pcmk_is_set(call_options, cib_dryrun)) {
// cib_dryrun implies a scratch copy by definition; no side effects
return true;
}
if (pcmk__str_eq(op, PCMK__CIB_REQUEST_COMMIT_TRANSACT, pcmk__str_none)) {
/* Commit-transaction must make a copy for atomicity. We must revert to
* the original CIB if the entire transaction cannot be applied
* successfully.
*/
return true;
}
if (pcmk_is_set(call_options, cib_transaction)) {
/* If cib_transaction is set, then we're in the process of committing a
* transaction. The commit-transaction request already made a scratch
* copy, and we're accumulating changes in that copy.
*/
return false;
}
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.
*
* @TODO: Is this safe? See discussion at
* https://github.com/ClusterLabs/pacemaker/pull/3094#discussion_r1211400690.
*/
return false;
}
// Default behavior is to operate on a scratch copy
return true;
}
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, xmlNode **output)
{
int rc = pcmk_ok;
bool check_schema = true;
bool make_copy = true;
xmlNode *top = NULL;
xmlNode *scratch = NULL;
xmlNode *patchset_cib = NULL;
xmlNode *local_diff = NULL;
const char *user = crm_element_value(req, PCMK__XA_CIB_USER);
bool with_digest = false;
crm_trace("Begin %s%s%s op",
(pcmk_is_set(call_options, cib_dryrun)? "dry run of " : ""),
(is_query? "read-only " : ""), op);
CRM_CHECK(output != NULL, return -ENOMSG);
CRM_CHECK(current_cib != NULL, return -ENOMSG);
CRM_CHECK(result_cib != NULL, return -ENOMSG);
CRM_CHECK(config_changed != NULL, return -ENOMSG);
if(output) {
*output = NULL;
}
*result_cib = NULL;
*config_changed = false;
if (fn == NULL) {
return -EINVAL;
}
if (is_query) {
xmlNode *cib_ro = *current_cib;
xmlNode *cib_filtered = NULL;
if (cib_acl_enabled(cib_ro, user)
&& xml_acl_filtered_copy(user, *current_cib, *current_cib,
&cib_filtered)) {
if (cib_filtered == NULL) {
crm_debug("Pre-filtered the entire cib");
return -EACCES;
}
cib_ro = cib_filtered;
crm_log_xml_trace(cib_ro, "filtered");
}
rc = (*fn) (op, call_options, section, req, input, cib_ro, result_cib, output);
if(output == NULL || *output == NULL) {
/* nothing */
} else if(cib_filtered == *output) {
cib_filtered = NULL; /* Let them have this copy */
} else if (*output == *current_cib) {
/* They already know not to free it */
} else if(cib_filtered && (*output)->doc == cib_filtered->doc) {
/* We're about to free the document of which *output is a part */
*output = pcmk__xml_copy(NULL, *output);
} else if ((*output)->doc == (*current_cib)->doc) {
/* Give them a copy they can free */
*output = pcmk__xml_copy(NULL, *output);
}
free_xml(cib_filtered);
return rc;
}
make_copy = should_copy_cib(op, section, call_options);
if (!make_copy) {
/* Conditional on v2 patch style */
scratch = *current_cib;
// Make a copy of the top-level element to store version details
top = pcmk__xe_create(NULL, (const char *) scratch->name);
copy_in_properties(top, scratch);
patchset_cib = top;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, scratch, &scratch, output);
/* If scratch points to a new object now (for example, after an erase
* operation), then *current_cib should point to the same object.
*/
*current_cib = scratch;
} else {
scratch = pcmk__xml_copy(NULL, *current_cib);
patchset_cib = *current_cib;
xml_track_changes(scratch, user, NULL, cib_acl_enabled(scratch, user));
rc = (*fn) (op, call_options, section, req, input, *current_cib,
&scratch, output);
if ((scratch != NULL) && !xml_tracking_changes(scratch)) {
crm_trace("Inferring changes after %s op", op);
xml_track_changes(scratch, user, *current_cib,
cib_acl_enabled(*current_cib, user));
xml_calculate_changes(*current_cib, scratch);
}
CRM_CHECK(*current_cib != scratch, return -EINVAL);
}
xml_acl_disable(scratch); /* Allow the system to make any additional changes */
if (rc == pcmk_ok && scratch == NULL) {
rc = -EINVAL;
goto done;
} else if(rc == pcmk_ok && xml_acl_denied(scratch)) {
crm_trace("ACL rejected part or all of the proposed changes");
rc = -EACCES;
goto done;
} else if (rc != pcmk_ok) {
goto done;
}
/* 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;
}
}
if (patchset_cib != NULL) {
int old = 0;
int new = 0;
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)",
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, 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)",
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;
}
}
}
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 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
*/
local_diff = xml_create_patchset(2, patchset_cib, scratch,
config_changed, manage_counters);
} else {
static time_t expires = 0;
time_t tm_now = time(NULL);
if (expires < tm_now) {
expires = tm_now + 60; /* Validate clients are correctly applying v2-style diffs at most once a minute */
with_digest = true;
}
local_diff = xml_create_patchset(0, patchset_cib, scratch,
config_changed, manage_counters);
}
pcmk__log_xml_changes(LOG_TRACE, scratch);
xml_accept_changes(scratch);
if(local_diff) {
patchset_process_digest(local_diff, patchset_cib, scratch, with_digest);
pcmk__log_xml_patchset(LOG_INFO, local_diff);
crm_log_xml_trace(local_diff, "raw patch");
}
if (make_copy && (local_diff != NULL)) {
// Original to compare against doesn't exist
pcmk__if_tracing(
{
// Validate the calculated patch set
int test_rc = pcmk_ok;
int format = 1;
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,
manage_counters);
if (test_rc != pcmk_ok) {
save_xml_to_file(cib_copy, "PatchApply:calculated", NULL);
save_xml_to_file(patchset_cib, "PatchApply:input", NULL);
save_xml_to_file(scratch, "PatchApply:actual", NULL);
save_xml_to_file(local_diff, "PatchApply:diff", NULL);
crm_err("v%d patchset error, patch failed to apply: %s "
"(%d)",
format, pcmk_rc_str(pcmk_legacy2rc(test_rc)),
test_rc);
}
free_xml(cib_copy);
},
{}
);
}
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
*/
check_schema = false;
}
/* === scratch must not be modified after this point ===
* Exceptions, anything in:
static filter_t filter[] = {
{ 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, PCMK_XA_VALIDATE_WITH);
pcmk__xe_add_last_written(scratch);
if (schema) {
static int minimum_schema = 0;
int current_schema = get_schema_version(schema);
if (minimum_schema == 0) {
minimum_schema = get_schema_version("pacemaker-1.2");
}
/* 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, 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);
}
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, 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)) {
const char *current_schema = crm_element_value(scratch,
PCMK_XA_VALIDATE_WITH);
crm_warn("Updated CIB does not validate against %s schema",
pcmk__s(current_schema, "unspecified"));
rc = -pcmk_err_schema_validation;
}
done:
*result_cib = scratch;
/* @TODO: This may not work correctly with !make_copy, since we don't
* keep the original CIB.
*/
if ((rc != pcmk_ok) && cib_acl_enabled(patchset_cib, user)
&& xml_acl_filtered_copy(user, patchset_cib, scratch, result_cib)) {
if (*result_cib == NULL) {
crm_debug("Pre-filtered the entire cib result");
}
free_xml(scratch);
}
if(diff) {
*diff = local_diff;
} else {
free_xml(local_diff);
}
free_xml(top);
crm_trace("Done");
return rc;
}
int
cib__create_op(cib_t *cib, const char *op, const char *host,
const char *section, xmlNode *data, int call_options,
const char *user_name, const char *client_name,
xmlNode **op_msg)
{
CRM_CHECK((cib != NULL) && (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, 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, PCMK__XA_CIB_CALLOPT, call_options);
if (data != NULL) {
add_message_xml(*op_msg, PCMK__XA_CIB_CALLDATA, data);
}
if (pcmk_is_set(call_options, cib_inhibit_bcast)) {
CRM_CHECK(pcmk_is_set(call_options, cib_scope_local),
free_xml(*op_msg); return -EPROTO);
}
return pcmk_ok;
}
/*!
* \internal
* \brief Check whether a CIB request is supported in a transaction
*
* \param[in] request CIB request
*
* \return Standard Pacemaker return code
*/
static int
validate_transaction_request(const xmlNode *request)
{
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);
if (rc != pcmk_rc_ok) {
// cib__get_operation() logs error
return rc;
}
if (!pcmk_is_set(operation->flags, cib__op_attr_transaction)) {
crm_err("Operation %s is not supported in CIB transactions", op);
return EOPNOTSUPP;
}
if (host != NULL) {
crm_err("Operation targeting a specific node (%s) is not supported in "
"a CIB transaction",
host);
return EOPNOTSUPP;
}
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Append a CIB request to a CIB transaction
*
* \param[in,out] cib CIB client whose transaction to extend
* \param[in,out] request Request to add to transaction
*
* \return Legacy Pacemaker return code
*/
int
cib__extend_transaction(cib_t *cib, xmlNode *request)
{
int rc = pcmk_rc_ok;
CRM_ASSERT((cib != NULL) && (request != NULL));
rc = validate_transaction_request(request);
if ((rc == pcmk_rc_ok) && (cib->transaction == NULL)) {
rc = pcmk_rc_no_transaction;
}
if (rc == pcmk_rc_ok) {
pcmk__xml_copy(cib->transaction, request);
} else {
const char *op = crm_element_value(request, PCMK__XA_CIB_OP);
const char *client_id = NULL;
cib->cmds->client_id(cib, NULL, &client_id);
crm_err("Failed to add '%s' operation to transaction for client %s: %s",
op, pcmk__s(client_id, "(unidentified)"), pcmk_rc_str(rc));
crm_log_xml_info(request, "failed");
}
return pcmk_rc2legacy(rc);
}
void
cib_native_callback(cib_t * cib, xmlNode * msg, int call_id, int rc)
{
xmlNode *output = NULL;
cib_callback_client_t *blob = NULL;
if (msg != NULL) {
crm_element_value_int(msg, PCMK__XA_CIB_RC, &rc);
crm_element_value_int(msg, PCMK__XA_CIB_CALLID, &call_id);
output = get_message_xml(msg, PCMK__XA_CIB_CALLDATA);
}
blob = cib__lookup_id(call_id);
if (blob == NULL) {
crm_trace("No callback found for call %d", call_id);
}
if (cib == NULL) {
crm_debug("No cib object supplied");
}
if (rc == -pcmk_err_diff_resync) {
/* This is an internal value that clients do not and should not care about */
rc = pcmk_ok;
}
if (blob && blob->callback && (rc == pcmk_ok || blob->only_success == FALSE)) {
crm_trace("Invoking callback %s for call %d",
pcmk__s(blob->id, "without ID"), call_id);
blob->callback(msg, call_id, rc, output, blob->user_data);
} else if (cib && cib->op_callback == NULL && rc != pcmk_ok) {
crm_warn("CIB command failed: %s", pcmk_strerror(rc));
crm_log_xml_debug(msg, "Failed CIB Update");
}
/* This may free user_data, so do it after the callback */
if (blob) {
remove_cib_op_callback(call_id, FALSE);
}
if (cib && cib->op_callback != NULL) {
crm_trace("Invoking global callback for call %d", call_id);
cib->op_callback(msg, call_id, rc, output);
}
crm_trace("OP callback activated for %d", call_id);
}
void
cib_native_notify(gpointer data, gpointer user_data)
{
xmlNode *msg = user_data;
cib_notify_client_t *entry = data;
const char *event = NULL;
if (msg == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(msg, PCMK__XA_SUBT);
if (entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if (entry->callback == NULL) {
crm_warn("Skipping callback - NULL callback");
return;
} else if (!pcmk__str_eq(entry->event, event, pcmk__str_casei)) {
crm_trace("Skipping callback - event mismatch %p/%s vs. %s", entry, entry->event, event);
return;
}
crm_trace("Invoking callback for %p/%s event...", entry, event);
entry->callback(event, msg);
crm_trace("Callback invoked...");
}
gboolean
cib_read_config(GHashTable * options, xmlNode * current_cib)
{
xmlNode *config = NULL;
crm_time_t *now = NULL;
if (options == NULL || current_cib == NULL) {
return FALSE;
}
now = crm_time_new(NULL);
g_hash_table_remove_all(options);
config = pcmk_find_cib_element(current_cib, PCMK_XE_CRM_CONFIG);
if (config) {
pe_unpack_nvpairs(current_cib, config, PCMK_XE_CLUSTER_PROPERTY_SET,
NULL, options, PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, TRUE,
now, NULL);
}
pcmk__validate_cluster_options(options);
crm_time_free(now);
return TRUE;
}
int
cib_internal_op(cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name)
{
int (*delegate) (cib_t * cib, const char *op, const char *host,
const char *section, xmlNode * data,
xmlNode ** output_data, int call_options, const char *user_name) =
cib->delegate_fn;
if(user_name == NULL) {
user_name = getenv("CIB_user");
}
return delegate(cib, op, host, section, data, output_data, call_options, user_name);
}
/*!
* \brief Apply a CIB update patch to a given CIB
*
* \param[in] event CIB update patch
* \param[in] input CIB to patch
* \param[out] output Resulting CIB after patch
* \param[in] level Log the patch at this log level (unless LOG_CRIT)
*
* \return Legacy Pacemaker return code
* \note sbd calls this function
*/
int
cib_apply_patch_event(xmlNode *event, xmlNode *input, xmlNode **output,
int level)
{
int rc = pcmk_err_generic;
xmlNode *diff = NULL;
CRM_ASSERT(event);
CRM_ASSERT(input);
CRM_ASSERT(output);
crm_element_value_int(event, PCMK__XA_CIB_RC, &rc);
diff = get_message_xml(event, PCMK__XA_CIB_UPDATE_RESULT);
if (rc < pcmk_ok || diff == NULL) {
return rc;
}
if (level > LOG_CRIT) {
pcmk__log_xml_patchset(level, diff);
}
if (input != NULL) {
rc = cib_process_diff(NULL, cib_none, NULL, event, diff, input, output,
NULL);
if (rc != pcmk_ok) {
crm_debug("Update didn't apply: %s (%d) %p",
pcmk_strerror(rc), rc, *output);
if (rc == -pcmk_err_old_data) {
crm_trace("Masking error, we already have the supplied update");
return pcmk_ok;
}
free_xml(*output);
*output = NULL;
return rc;
}
}
return rc;
}
#define log_signon_query_err(out, fmt, args...) do { \
if (out != NULL) { \
out->err(out, fmt, ##args); \
} else { \
crm_err(fmt, ##args); \
} \
} while (0)
int
cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
{
int rc = pcmk_rc_ok;
cib_t *cib_conn = NULL;
CRM_ASSERT(cib_object != NULL);
if (cib == NULL) {
cib_conn = cib_new();
} else {
if (*cib == NULL) {
*cib = cib_new();
}
cib_conn = *cib;
}
if (cib_conn == NULL) {
return ENOMEM;
}
if (cib_conn->state == cib_disconnected) {
rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
rc = pcmk_legacy2rc(rc);
}
if (rc != pcmk_rc_ok) {
log_signon_query_err(out, "Could not connect to the CIB: %s",
pcmk_rc_str(rc));
goto done;
}
if (out != NULL) {
out->transient(out, "Querying CIB...");
}
rc = cib_conn->cmds->query(cib_conn, NULL, cib_object,
cib_scope_local|cib_sync_call);
rc = pcmk_legacy2rc(rc);
if (rc != pcmk_rc_ok) {
log_signon_query_err(out, "CIB query failed: %s", pcmk_rc_str(rc));
}
done:
if (cib == NULL) {
cib__clean_up_connection(&cib_conn);
}
if ((rc == pcmk_rc_ok) && (*cib_object == NULL)) {
return pcmk_rc_no_input;
}
return rc;
}
int
cib__clean_up_connection(cib_t **cib)
{
int rc;
if (*cib == NULL) {
return pcmk_rc_ok;
}
rc = (*cib)->cmds->signoff(*cib);
cib_delete(*cib);
*cib = NULL;
return pcmk_legacy2rc(rc);
}
// Deprecated functions kept only for backward API compatibility
// LCOV_EXCL_START
#include
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) {
copy_in_properties(generation, the_cib);
free_xml(the_cib);
}
return generation;
}
const char *
get_object_path(const char *object_type)
{
return pcmk_cib_xpath_for(object_type);
}
const char *
get_object_parent(const char *object_type)
{
return pcmk_cib_parent_name_for(object_type);
}
xmlNode *
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/common/tests/actions/Makefile.am b/lib/common/tests/actions/Makefile.am
index 03d3ea4f99..89f1e1c57a 100644
--- a/lib/common/tests/actions/Makefile.am
+++ b/lib/common/tests/actions/Makefile.am
@@ -1,19 +1,18 @@
#
# 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 = copy_in_properties_test \
expand_plus_plus_test \
- fix_plus_plus_recursive_test \
parse_op_key_test
TESTS = $(check_PROGRAMS)
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 db844600c0..0000000000
--- a/lib/common/tests/actions/fix_plus_plus_recursive_test.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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
-
-#include
-
-#include
-
-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 = pcmk__xe_create(NULL, "test_xml_root");
- xmlNode *test_xml_child = pcmk__xe_create(test_xml_root, "test_xml_child");
- xmlNode *test_xml_grandchild = pcmk__xe_create(test_xml_child,
- "test_xml_grandchild");
- xmlNode *test_xml_text = pcmk__xe_create(test_xml_root, "text_xml_text");
- xmlNode *test_xml_comment = pcmk__xml_parse("");
-
- pcmk__xe_set_content(test_xml_text, "content");
-
- 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/xml.c b/lib/common/xml.c
index 715e4f735b..b916116b8d 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -1,2660 +1,2652 @@
/*
* 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
#include
#include
#include
#include
#include
#include // stat(), S_ISREG, etc.
#include
#include
#include
#include
#include
#include // PCMK__XML_LOG_BASE, etc.
#include "crmcommon_private.h"
/*!
* \internal
* \brief Apply a function to each XML node in a tree (pre-order, depth-first)
*
* \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.
*/
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)
{
if(xml == NULL || xml->doc == NULL || xml->doc->_private == NULL) {
return FALSE;
} else if (!pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
pcmk__xf_tracking)) {
return FALSE;
} else if (lazy && !pcmk_is_set(((xml_doc_private_t *)xml->doc->_private)->flags,
pcmk__xf_lazy)) {
return FALSE;
}
return TRUE;
}
static inline void
set_parent_flag(xmlNode *xml, long flag)
{
for(; xml; xml = xml->parent) {
xml_node_private_t *nodepriv = xml->_private;
if (nodepriv == NULL) {
/* During calls to xmlDocCopyNode(), _private will be unset for parent nodes */
} else {
pcmk__set_xml_flags(nodepriv, flag);
}
}
}
void
pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
{
if(xml && xml->doc && xml->doc->_private){
/* During calls to xmlDocCopyNode(), xml->doc may be unset */
xml_doc_private_t *docpriv = xml->doc->_private;
pcmk__set_xml_flags(docpriv, flag);
}
}
// Mark document, element, and all element's parents as changed
void
pcmk__mark_xml_node_dirty(xmlNode *xml)
{
pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty);
set_parent_flag(xml, pcmk__xf_dirty);
}
// Clear flags on XML node and its children
static void
reset_xml_node_flags(xmlNode *xml)
{
xmlNode *cIter = NULL;
xml_node_private_t *nodepriv = xml->_private;
if (nodepriv) {
nodepriv->flags = 0;
}
for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
cIter = pcmk__xml_next(cIter)) {
reset_xml_node_flags(cIter);
}
}
// Set xpf_created flag on XML node and any children
void
pcmk__mark_xml_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);
}
}
}
#define XML_DOC_PRIVATE_MAGIC 0x81726354UL
#define XML_NODE_PRIVATE_MAGIC 0x54637281UL
// Free an XML object previously marked as deleted
static void
free_deleted_object(void *data)
{
if(data) {
pcmk__deleted_xml_t *deleted_obj = data;
g_free(deleted_obj->path);
free(deleted_obj);
}
}
// Free and NULL user, ACLs, and deleted objects in an XML node's private data
static void
reset_xml_private_data(xml_doc_private_t *docpriv)
{
if (docpriv != NULL) {
CRM_ASSERT(docpriv->check == XML_DOC_PRIVATE_MAGIC);
free(docpriv->user);
docpriv->user = NULL;
if (docpriv->acls != NULL) {
pcmk__free_acls(docpriv->acls);
docpriv->acls = NULL;
}
if(docpriv->deleted_objs) {
g_list_free_full(docpriv->deleted_objs, free_deleted_object);
docpriv->deleted_objs = NULL;
}
}
}
// Free all private data associated with an XML node
static void
free_private_data(xmlNode *node)
{
/* Note:
This function frees private data assosciated with an XML node,
unless the function is being called as a result of internal
XSLT cleanup.
That could happen through, for example, the following chain of
function calls:
xsltApplyStylesheetInternal
-> xsltFreeTransformContext
-> xsltFreeRVTs
-> xmlFreeDoc
And in that case, the node would fulfill three conditions:
1. It would be a standalone document (i.e. it wouldn't be
part of a document)
2. It would have a space-prefixed name (for reference, please
see xsltInternals.h: XSLT_MARK_RES_TREE_FRAG)
3. It would carry its own payload in the _private field.
We do not free data in this circumstance to avoid a failed
assertion on the XML_*_PRIVATE_MAGIC later.
*/
if (node->name == NULL || node->name[0] != ' ') {
if (node->_private) {
if (node->type == XML_DOCUMENT_NODE) {
reset_xml_private_data(node->_private);
} else {
CRM_ASSERT(((xml_node_private_t *) node->_private)->check
== XML_NODE_PRIVATE_MAGIC);
/* nothing dynamically allocated nested */
}
free(node->_private);
node->_private = NULL;
}
}
}
// Allocate and initialize private data for an XML node
static void
new_private_data(xmlNode *node)
{
switch (node->type) {
case XML_DOCUMENT_NODE: {
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);
node->_private = docpriv;
break;
}
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_COMMENT_NODE: {
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);
node->_private = nodepriv;
if (pcmk__tracking_xml_changes(node, FALSE)) {
/* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
* not hooked up at the point we are called
*/
pcmk__mark_xml_node_dirty(node);
}
break;
}
case XML_TEXT_NODE:
case XML_DTD_NODE:
case XML_CDATA_SECTION_NODE:
break;
default:
/* Ignore */
crm_trace("Ignoring %p %d", node, node->type);
CRM_LOG_ASSERT(node->type == XML_ELEMENT_NODE);
break;
}
}
void
xml_track_changes(xmlNode * xml, const char *user, xmlNode *acl_source, bool enforce_acls)
{
xml_accept_changes(xml);
crm_trace("Tracking changes%s to %p", enforce_acls?" with ACLs":"", xml);
pcmk__set_xml_doc_flag(xml, pcmk__xf_tracking);
if(enforce_acls) {
if(acl_source == NULL) {
acl_source = xml;
}
pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_enabled);
pcmk__unpack_acl(acl_source, xml, user);
pcmk__apply_acl(xml);
}
}
bool xml_tracking_changes(xmlNode * xml)
{
return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
&& pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
pcmk__xf_tracking);
}
bool xml_document_dirty(xmlNode *xml)
{
return (xml != NULL) && (xml->doc != NULL) && (xml->doc->_private != NULL)
&& pcmk_is_set(((xml_doc_private_t *)(xml->doc->_private))->flags,
pcmk__xf_dirty);
}
/*!
* \internal
* \brief Return ordinal position of an XML node among its siblings
*
* \param[in] xml XML node to check
* \param[in] ignore_if_set Don't count siblings with this flag set
*
* \return Ordinal position of \p xml (starting with 0)
*/
int
pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
{
int position = 0;
for (const xmlNode *cIter = xml; cIter->prev; cIter = cIter->prev) {
xml_node_private_t *nodepriv = ((xmlNode*)cIter->prev)->_private;
if (!pcmk_is_set(nodepriv->flags, ignore_if_set)) {
position++;
}
}
return position;
}
// Remove all attributes marked as deleted from an XML node
static void
accept_attr_deletions(xmlNode *xml)
{
// 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
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);
}
}
/*!
* \internal
* \brief Find first child XML node matching another given XML node
*
* \param[in] haystack XML whose children should be checked
* \param[in] needle XML to match (comment content or element name and ID)
* \param[in] exact If true and needle is a comment, position must match
*/
xmlNode *
pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
{
CRM_CHECK(needle != NULL, return NULL);
if (needle->type == XML_COMMENT_NODE) {
return pcmk__xc_match(haystack, needle, exact);
} else {
const char *id = pcmk__xe_id(needle);
const char *attr = (id == NULL)? NULL : PCMK_XA_ID;
return pcmk__xe_first_child(haystack, (const char *) needle->name, attr,
id);
}
}
void
xml_accept_changes(xmlNode * xml)
{
xmlNode *top = NULL;
xml_doc_private_t *docpriv = NULL;
if(xml == NULL) {
return;
}
crm_trace("Accepting changes to %p", xml);
docpriv = xml->doc->_private;
top = xmlDocGetRootElement(xml->doc);
reset_xml_private_data(xml->doc->_private);
if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
docpriv->flags = pcmk__xf_none;
return;
}
docpriv->flags = pcmk__xf_none;
accept_attr_deletions(top);
}
/*!
* \internal
* \brief Find first XML child element matching given criteria
*
* \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 \c NULL if none found
*/
xmlNode *
pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
const char *attr_n, const char *attr_v)
{
xmlNode *child = NULL;
const char *parent_name = "";
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;
}
for (; child != NULL; child = pcmk__xe_next(child)) {
const char *value = NULL;
if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
// Node name mismatch
continue;
}
if (attr_n == NULL) {
// No attribute match needed
return child;
}
value = crm_element_value(child, attr_n);
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;
}
}
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;
}
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;
}
}
}
return;
}
-/*!
- * \brief Parse integer assignment statements on this node and all its child
- * nodes
- *
- * \param[in,out] target Root XML node to be processed
- *
- * \note This function is recursive
- */
-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);
- }
-}
-
/*!
* \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.
*/
void
expand_plus_plus(xmlNode * target, const char *name, const char *value)
{
int offset = 1;
int name_len = 0;
int int_value = 0;
int value_len = 0;
const char *old_value = NULL;
if (target == NULL || value == NULL || name == NULL) {
return;
}
old_value = crm_element_value(target, name);
if (old_value == NULL) {
/* if no previous value, set unexpanded */
goto set_unexpanded;
} else if (strstr(value, name) != value) {
goto set_unexpanded;
}
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;
}
/* 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);
}
if (value[name_len + 1] != '+') {
const char *offset_s = value + (name_len + 2);
offset = char2score(offset_s);
}
int_value += offset;
if (int_value > PCMK_SCORE_INFINITY) {
int_value = PCMK_SCORE_INFINITY;
}
crm_xml_add_int(target, name, int_value);
return;
set_unexpanded:
if (old_value == value) {
/* the old value is already set, nothing to do */
return;
}
crm_xml_add(target, name, value);
return;
}
/*!
* \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 (!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;
}
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);
}
return pcmk_rc_ok;
}
/*!
* \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));
}
}
/*!
* \internal
* \brief Remove an XML element's attributes that match some criteria
*
* \param[in,out] element XML element to modify
* \param[in] match If not NULL, only remove attributes for which
* this function returns true
* \param[in,out] user_data Data to pass to \p match
*/
void
pcmk__xe_remove_matching_attrs(xmlNode *element,
bool (*match)(xmlAttrPtr, void *),
void *user_data)
{
xmlAttrPtr next = NULL;
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 (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 *
pcmk__xe_create(xmlNode *parent, const char *name)
{
xmlNode *node = NULL;
CRM_ASSERT(!pcmk__str_empty(name));
if (parent == NULL) {
xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
pcmk__mem_assert(doc);
node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
pcmk__mem_assert(node);
xmlDocSetRootElement(doc, node);
} else {
node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
pcmk__mem_assert(node);
}
pcmk__mark_xml_created(node);
return node;
}
/*!
* \internal
* \brief Set a formatted string as an XML node's content
*
* \param[in,out] node Node whose content to set
* \param[in] format printf(3)-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, ...)
{
if (node != NULL) {
const char *content = NULL;
char *buf = NULL;
if (strchr(format, '%') == NULL) {
// Nothing to format
content = format;
} else {
va_list ap;
va_start(ap, format);
if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
// No need to make a copy
content = va_arg(ap, const char *);
} else {
CRM_ASSERT(vasprintf(&buf, format, ap) >= 0);
content = buf;
}
va_end(ap);
}
xmlNodeSetContent(node, (pcmkXmlStr) content);
free(buf);
}
}
/*!
* Free an XML element and all of its children, removing it from its parent
*
* \param[in,out] xml XML element to free
*/
void
pcmk_free_xml_subtree(xmlNode *xml)
{
xmlUnlinkNode(xml); // Detaches from parent and siblings
xmlFreeNode(xml); // Frees
}
static void
free_xml_with_position(xmlNode *child, int position)
{
xmlDoc *doc = NULL;
xml_node_private_t *nodepriv = NULL;
if (child == NULL) {
return;
}
doc = child->doc;
nodepriv = child->_private;
if ((doc != NULL) && (xmlDocGetRootElement(doc) == child)) {
// Free everything
xmlFreeDoc(doc);
return;
}
if (!pcmk__check_acl(child, NULL, pcmk__xf_acl_write)) {
GString *xpath = NULL;
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 ((doc != NULL) && pcmk__tracking_xml_changes(child, false)
&& !pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
xml_doc_private_t *docpriv = doc->_private;
GString *xpath = pcmk__element_xpath(child);
if (xpath != NULL) {
pcmk__deleted_xml_t *deleted_obj = NULL;
crm_trace("Deleting %s %p from %p", xpath->str, child, doc);
deleted_obj = pcmk__assert_alloc(1, sizeof(pcmk__deleted_xml_t));
deleted_obj->path = g_string_free(xpath, FALSE);
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;
} else {
deleted_obj->position = pcmk__xml_position(child,
pcmk__xf_skip);
}
}
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);
}
void
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 *
pcmk__xml_copy(xmlNode *parent, xmlNode *src)
{
xmlNode *copy = NULL;
if (src == NULL) {
return NULL;
}
if (parent == NULL) {
xmlDoc *doc = NULL;
// The copy will be the root element of a new document
CRM_ASSERT(src->type == XML_ELEMENT_NODE);
doc = xmlNewDoc(PCMK__XML_VERSION);
pcmk__mem_assert(doc);
copy = xmlDocCopyNode(src, doc, 1);
pcmk__mem_assert(copy);
xmlDocSetRootElement(doc, copy);
} else {
copy = xmlDocCopyNode(src, parent->doc, 1);
pcmk__mem_assert(copy);
xmlAddChild(parent, copy);
}
pcmk__mark_xml_created(copy);
return copy;
}
/*!
* \internal
* \brief Remove XML text nodes from specified XML and all its children
*
* \param[in,out] xml XML to strip text from
*/
void
pcmk__strip_xml_text(xmlNode *xml)
{
xmlNode *iter = xml->children;
while (iter) {
xmlNode *next = iter->next;
switch (iter->type) {
case XML_TEXT_NODE:
/* Remove it */
pcmk_free_xml_subtree(iter);
break;
case XML_ELEMENT_NODE:
/* Search it */
pcmk__strip_xml_text(iter);
break;
default:
/* Leave it */
break;
}
iter = next;
}
}
/*!
* \internal
* \brief Add a "last written" attribute to an XML element, set to current time
*
* \param[in,out] xe XML element to add attribute to
*
* \return Value that was set, or NULL on error
*/
const char *
pcmk__xe_add_last_written(xmlNode *xe)
{
char *now_s = pcmk__epoch2str(NULL, 0);
const char *result = NULL;
result = crm_xml_add(xe, PCMK_XA_CIB_LAST_WRITTEN,
pcmk__s(now_s, "Could not determine current time"));
free(now_s);
return result;
}
/*!
* \brief Sanitize a string so it is usable as an XML ID
*
* \param[in,out] id String to sanitize
*/
void
crm_xml_sanitize_id(char *id)
{
char *c;
for (c = id; *c; ++c) {
/* @TODO Sanitize more comprehensively */
switch (*c) {
case ':':
case '#':
*c = '.';
}
}
}
/*!
* \brief Set the ID of an XML element using a format
*
* \param[in,out] xml XML element
* \param[in] fmt printf-style format
* \param[in] ... any arguments required by format
*/
void
crm_xml_set_id(xmlNode *xml, const char *format, ...)
{
va_list ap;
int len = 0;
char *id = NULL;
/* equivalent to crm_strdup_printf() */
va_start(ap, format);
len = vasprintf(&id, format, ap);
va_end(ap);
CRM_ASSERT(len > 0);
crm_xml_sanitize_id(id);
crm_xml_add(xml, PCMK_XA_ID, id);
free(id);
}
/*!
* \internal
* \brief Get consecutive bytes encoding non-ASCII UTF-8 characters
*
* \param[in] text String to check
*
* \return Number of non-ASCII UTF-8 bytes at the beginning of \p text
*/
static size_t
utf8_bytes(const char *text)
{
// Total number of consecutive bytes containing UTF-8 characters
size_t c_bytes = 0;
if (text == NULL) {
return 0;
}
/* UTF-8 uses one to four 8-bit bytes per character. The first byte
* indicates the width of the character. A byte beginning with a '0' bit is
* a one-byte ASCII character.
*
* A C byte is 8 bits on most systems, but this is not guaranteed.
*
* Count until we find an ASCII character or an invalid byte. Check bytes
* aligned with the C byte boundary.
*/
for (const uint8_t *utf8_byte = (const uint8_t *) text;
(*utf8_byte & 0x80) != 0;
utf8_byte = (const uint8_t *) (text + c_bytes)) {
size_t utf8_bits = 0;
if ((*utf8_byte & 0xf0) == 0xf0) {
// Four-byte character (first byte: 11110xxx)
utf8_bits = 32;
} else if ((*utf8_byte & 0xe0) == 0xe0) {
// Three-byte character (first byte: 1110xxxx)
utf8_bits = 24;
} else if ((*utf8_byte & 0xc0) == 0xc0) {
// Two-byte character (first byte: 110xxxxx)
utf8_bits = 16;
} else {
crm_warn("Found invalid UTF-8 character %.2x",
(unsigned char) *utf8_byte);
return c_bytes;
}
c_bytes += utf8_bits / CHAR_BIT;
#if (CHAR_BIT != 8) // Coverity complains about dead code without this CPP guard
if ((utf8_bits % CHAR_BIT) > 0) {
c_bytes++;
}
#endif // CHAR_BIT != 8
}
for (int i = 0; i < c_bytes; i++) {
// Sanity-check that we haven't passed end of string as "non-ASCII"
CRM_ASSERT(text[i] != '\0');
}
return c_bytes;
}
/*!
* \internal
* \brief Check whether a string has XML special characters that must be escaped
*
* See \c pcmk__xml_escape() and \c pcmk__xml_escape_type for more details.
*
* \param[in] text String to check
* \param[in] type Type of escaping
*
* \return \c true if \p text has special characters that need to be escaped, or
* \c false otherwise
*/
bool
pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type)
{
if (text == NULL) {
return false;
}
while (*text != '\0') {
// Don't escape any non-ASCII characters
size_t ubytes = utf8_bytes(text);
if (ubytes > 0) {
text += ubytes;
continue;
}
switch (type) {
case pcmk__xml_escape_text:
switch (*text) {
case '<':
case '>':
case '&':
return true;
case '\n':
case '\t':
break;
default:
if (!isprint(*text)) {
return true;
}
break;
}
break;
case pcmk__xml_escape_attr:
switch (*text) {
case '<':
case '>':
case '&':
case '"':
return true;
default:
if (!isprint(*text)) {
return true;
}
break;
}
break;
case pcmk__xml_escape_attr_pretty:
switch (*text) {
case '\n':
case '\r':
case '\t':
case '"':
return true;
default:
break;
}
break;
default: // Invalid enum value
CRM_ASSERT(false);
break;
}
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 \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().
*/
gchar *
pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
{
GString *copy = NULL;
if (text == NULL) {
return NULL;
}
copy = g_string_sized_new(strlen(text));
while (*text != '\0') {
// Don't escape any non-ASCII characters
size_t ubytes = utf8_bytes(text);
if (ubytes > 0) {
g_string_append_len(copy, text, ubytes);
text += ubytes;
continue;
}
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 (!isprint(*text)) {
g_string_append_printf(copy, "%.2X;", *text);
} else {
g_string_append_c(copy, *text);
}
break;
}
break;
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 (!isprint(*text)) {
g_string_append_printf(copy, "%.2X;", *text);
} else {
g_string_append_c(copy, *text);
}
break;
}
break;
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;
default: // Invalid enum value
CRM_ASSERT(false);
break;
}
text++;
}
return g_string_free(copy, FALSE);
}
/*!
* \internal
* \brief Set a flag on all attributes of an XML element
*
* \param[in,out] xml XML node to set flags on
* \param[in] flag XML private flag to set
*/
static void
set_attrs_flag(xmlNode *xml, enum xml_private_flags flag)
{
for (xmlAttr *attr = pcmk__xe_first_attr(xml); attr; attr = attr->next) {
pcmk__set_xml_flags((xml_node_private_t *) (attr->_private), flag);
}
}
/*!
* \internal
* \brief Add an XML attribute to a node, marked as deleted
*
* When calculating XML changes, we need to know when an attribute has been
* deleted. Add the attribute back to the new XML, so that we can check the
* removal against ACLs, and mark it as deleted for later removal after
* differences have been calculated.
*
* \param[in,out] new_xml XML to modify
* \param[in] element Name of XML element that changed (for logging)
* \param[in] attr_name Name of attribute that was deleted
* \param[in] old_value Value of attribute that was deleted
*/
static void
mark_attr_deleted(xmlNode *new_xml, const char *element, const char *attr_name,
const char *old_value)
{
xml_doc_private_t *docpriv = new_xml->doc->_private;
xmlAttr *attr = NULL;
xml_node_private_t *nodepriv;
// Prevent the dirty flag being set recursively upwards
pcmk__clear_xml_flags(docpriv, pcmk__xf_tracking);
// Restore the old value (and the tracking flag)
attr = xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
pcmk__set_xml_flags(docpriv, pcmk__xf_tracking);
// Reset flags (so the attribute doesn't appear as newly created)
nodepriv = attr->_private;
nodepriv->flags = 0;
// Check ACLs and mark restored value for later removal
remove_xe_attr(new_xml, attr);
crm_trace("XML attribute %s=%s was removed from %s",
attr_name, old_value, element);
}
/*
* \internal
* \brief Check ACLs for a changed XML attribute
*/
static void
mark_attr_changed(xmlNode *new_xml, const char *element, const char *attr_name,
const char *old_value)
{
char *vcopy = crm_element_value_copy(new_xml, attr_name);
crm_trace("XML attribute %s was changed from '%s' to '%s' in %s",
attr_name, old_value, vcopy, element);
// Restore the original value
xmlSetProp(new_xml, (pcmkXmlStr) attr_name, (pcmkXmlStr) old_value);
// Change it back to the new value, to check ACLs
crm_xml_add(new_xml, attr_name, vcopy);
free(vcopy);
}
/*!
* \internal
* \brief Mark an XML attribute as having changed position
*
* \param[in,out] new_xml XML to modify
* \param[in] element Name of XML element that changed (for logging)
* \param[in,out] old_attr Attribute that moved, in original XML
* \param[in,out] new_attr Attribute that moved, in \p new_xml
* \param[in] p_old Ordinal position of \p old_attr in original XML
* \param[in] p_new Ordinal position of \p new_attr in \p new_xml
*/
static void
mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
xmlAttr *new_attr, int p_old, int p_new)
{
xml_node_private_t *nodepriv = new_attr->_private;
crm_trace("XML attribute %s moved from position %d to %d in %s",
old_attr->name, p_old, p_new, element);
// Mark document, element, and all element's parents as changed
pcmk__mark_xml_node_dirty(new_xml);
// Mark attribute as changed
pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved);
nodepriv = (p_old > p_new)? old_attr->_private : new_attr->_private;
pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
}
/*!
* \internal
* \brief Calculate differences in all previously existing XML attributes
*
* \param[in,out] old_xml Original XML to compare
* \param[in,out] new_xml New XML to compare
*/
static void
xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
{
xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
while (attr_iter != NULL) {
const char *name = (const char *) attr_iter->name;
xmlAttr *old_attr = attr_iter;
xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
const char *old_value = pcmk__xml_attr_value(attr_iter);
attr_iter = attr_iter->next;
if (new_attr == NULL) {
mark_attr_deleted(new_xml, (const char *) old_xml->name, name,
old_value);
} else {
xml_node_private_t *nodepriv = new_attr->_private;
int new_pos = pcmk__xml_position((xmlNode*) new_attr,
pcmk__xf_skip);
int old_pos = pcmk__xml_position((xmlNode*) old_attr,
pcmk__xf_skip);
const char *new_value = crm_element_value(new_xml, name);
// This attribute isn't new
pcmk__clear_xml_flags(nodepriv, pcmk__xf_created);
if (strcmp(new_value, old_value) != 0) {
mark_attr_changed(new_xml, (const char *) old_xml->name, name,
old_value);
} else if ((old_pos != new_pos)
&& !pcmk__tracking_xml_changes(new_xml, TRUE)) {
mark_attr_moved(new_xml, (const char *) old_xml->name,
old_attr, new_attr, old_pos, new_pos);
}
}
}
}
/*!
* \internal
* \brief Check all attributes in new XML for creation
*
* For each of a given XML element's attributes marked as newly created, accept
* (and mark as dirty) or reject the creation according to ACLs.
*
* \param[in,out] new_xml XML to check
*/
static void
mark_created_attrs(xmlNode *new_xml)
{
xmlAttr *attr_iter = pcmk__xe_first_attr(new_xml);
while (attr_iter != NULL) {
xmlAttr *new_attr = attr_iter;
xml_node_private_t *nodepriv = attr_iter->_private;
attr_iter = attr_iter->next;
if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
const char *attr_name = (const char *) new_attr->name;
crm_trace("Created new attribute %s=%s in %s",
attr_name, pcmk__xml_attr_value(new_attr),
new_xml->name);
/* Check ACLs (we can't use the remove-then-create trick because it
* would modify the attribute position).
*/
if (pcmk__check_acl(new_xml, attr_name, pcmk__xf_acl_write)) {
pcmk__mark_xml_attr_dirty(new_attr);
} else {
// Creation was not allowed, so remove the attribute
xmlUnsetProp(new_xml, new_attr->name);
}
}
}
}
/*!
* \internal
* \brief Calculate differences in attributes between two XML nodes
*
* \param[in,out] old_xml Original XML to compare
* \param[in,out] new_xml New XML to compare
*/
static void
xml_diff_attrs(xmlNode *old_xml, xmlNode *new_xml)
{
set_attrs_flag(new_xml, pcmk__xf_created); // cleared later if not really new
xml_diff_old_attrs(old_xml, new_xml);
mark_created_attrs(new_xml);
}
/*!
* \internal
* \brief Add an XML child element to a node, marked as deleted
*
* When calculating XML changes, we need to know when a child element has been
* deleted. Add the child back to the new XML, so that we can check the removal
* against ACLs, and mark it as deleted for later removal after differences have
* been calculated.
*
* \param[in,out] old_child Child element from original XML
* \param[in,out] new_parent New XML to add marked copy to
*/
static void
mark_child_deleted(xmlNode *old_child, xmlNode *new_parent)
{
// Re-create the child element so we can check ACLs
xmlNode *candidate = pcmk__xml_copy(new_parent, old_child);
// Clear flags on new child and its children
reset_xml_node_flags(candidate);
// Check whether ACLs allow the deletion
pcmk__apply_acl(xmlDocGetRootElement(candidate->doc));
// Remove the child again (which will track it in document's deleted_objs)
free_xml_with_position(candidate,
pcmk__xml_position(old_child, pcmk__xf_skip));
if (pcmk__xml_match(new_parent, old_child, true) == NULL) {
pcmk__set_xml_flags((xml_node_private_t *) (old_child->_private),
pcmk__xf_skip);
}
}
static void
mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
int p_old, int p_new)
{
xml_node_private_t *nodepriv = new_child->_private;
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), ""),
p_old, p_new, new_parent->name);
pcmk__mark_xml_node_dirty(new_parent);
pcmk__set_xml_flags(nodepriv, pcmk__xf_moved);
if (p_old > p_new) {
nodepriv = old_child->_private;
} else {
nodepriv = new_child->_private;
}
pcmk__set_xml_flags(nodepriv, pcmk__xf_skip);
}
// Given original and new XML, mark new XML portions that have changed
static void
mark_xml_changes(xmlNode *old_xml, xmlNode *new_xml, bool check_top)
{
xmlNode *cIter = NULL;
xml_node_private_t *nodepriv = NULL;
CRM_CHECK(new_xml != NULL, return);
if (old_xml == NULL) {
pcmk__mark_xml_created(new_xml);
pcmk__apply_creation_acl(new_xml, check_top);
return;
}
nodepriv = new_xml->_private;
CRM_CHECK(nodepriv != NULL, return);
if(nodepriv->flags & pcmk__xf_processed) {
/* Avoid re-comparing nodes */
return;
}
pcmk__set_xml_flags(nodepriv, pcmk__xf_processed);
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);
cIter = pcmk__xml_next(cIter);
if(new_child) {
mark_xml_changes(old_child, new_child, TRUE);
} else {
mark_child_deleted(old_child, new_xml);
}
}
// 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);
cIter = pcmk__xml_next(cIter);
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);
} else {
/* Check for movement, we already checked for differences */
int p_new = pcmk__xml_position(new_child, pcmk__xf_skip);
int p_old = pcmk__xml_position(old_child, pcmk__xf_skip);
if(p_old != p_new) {
mark_child_moved(old_child, new_xml, new_child, p_old, p_new);
}
}
}
}
void
xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
{
pcmk__set_xml_doc_flag(new_xml, pcmk__xf_lazy);
xml_calculate_changes(old_xml, new_xml);
}
// Called functions may set the \p pcmk__xf_skip flag on parts of \p old_xml
void
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(pcmk__xe_id(old_xml), pcmk__xe_id(new_xml),
pcmk__str_none),
return);
if(xml_tracking_changes(new_xml) == FALSE) {
xml_track_changes(new_xml, NULL, NULL, FALSE);
}
mark_xml_changes(old_xml, new_xml, FALSE);
}
/*!
* \internal
* \brief Find a comment with matching content in specified XML
*
* \param[in] root XML to search
* \param[in] search_comment Comment whose content should be searched for
* \param[in] exact If true, comment must also be at same position
*/
xmlNode *
pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment, bool exact)
{
xmlNode *a_child = NULL;
int search_offset = pcmk__xml_position(search_comment, pcmk__xf_skip);
CRM_CHECK(search_comment->type == XML_COMMENT_NODE, return NULL);
for (a_child = pcmk__xml_first_child(root); a_child != NULL;
a_child = pcmk__xml_next(a_child)) {
if (exact) {
int offset = pcmk__xml_position(a_child, pcmk__xf_skip);
xml_node_private_t *nodepriv = a_child->_private;
if (offset < search_offset) {
continue;
} else if (offset > search_offset) {
return NULL;
}
if (pcmk_is_set(nodepriv->flags, pcmk__xf_skip)) {
continue;
}
}
if (a_child->type == XML_COMMENT_NODE
&& pcmk__str_eq((const char *)a_child->content, (const char *)search_comment->content, pcmk__str_casei)) {
return a_child;
} else if (exact) {
return NULL;
}
}
return NULL;
}
/*!
* \internal
* \brief Make one XML comment match another (in content)
*
* \param[in,out] parent If \p target is NULL and this is not, add or update
* comment child of this XML node that matches \p update
* \param[in,out] target If not NULL, update this XML comment node
* \param[in] update Make comment content match this (must not be NULL)
*
* \note At least one of \parent and \target must be non-NULL
*/
void
pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
{
CRM_CHECK(update != NULL, return);
CRM_CHECK(update->type == XML_COMMENT_NODE, return);
if (target == NULL) {
target = pcmk__xc_match(parent, update, false);
}
if (target == NULL) {
pcmk__xml_copy(parent, update);
} else if (!pcmk__str_eq((const char *)target->content, (const char *)update->content, pcmk__str_casei)) {
xmlFree(target->content);
target->content = xmlStrdup(update->content);
}
}
/*!
* \internal
* \brief Make one XML tree match another (in children and attributes)
*
* \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
*
* \note At least one of \p parent and \p target must be non-NULL
*/
void
pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
bool as_diff)
{
xmlNode *a_child = NULL;
const char *object_name = NULL,
*object_href = NULL,
*object_href_val = NULL;
crm_log_xml_trace(update, "update:");
crm_log_xml_trace(target, "target:");
CRM_CHECK(update != NULL, return);
if (update->type == XML_COMMENT_NODE) {
pcmk__xc_update(parent, target, update);
return;
}
object_name = (const char *) update->name;
object_href_val = pcmk__xe_id(update);
if (object_href_val != NULL) {
object_href = PCMK_XA_ID;
} else {
object_href_val = crm_element_value(update, PCMK_XA_ID_REF);
object_href = (object_href_val == NULL)? NULL : PCMK_XA_ID_REF;
}
CRM_CHECK(object_name != NULL, return);
CRM_CHECK(target != NULL || parent != NULL, return);
if (target == NULL) {
target = pcmk__xe_first_child(parent, object_name,
object_href, object_href_val);
}
if (target == NULL) {
target = pcmk__xe_create(parent, object_name);
crm_trace("Added <%s%s%s%s%s/>", pcmk__s(object_name, ""),
object_href ? " " : "",
object_href ? object_href : "",
object_href ? "=" : "",
object_href ? object_href_val : "");
} else {
crm_trace("Found node <%s%s%s%s%s/> to update",
pcmk__s(object_name, ""),
object_href ? " " : "",
object_href ? object_href : "",
object_href ? "=" : "",
object_href ? object_href_val : "");
}
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);
} else {
/* No need for expand_plus_plus(), just raw speed */
for (xmlAttrPtr a = pcmk__xe_first_attr(update); a != NULL;
a = a->next) {
const char *p_value = pcmk__xml_attr_value(a);
/* Remove it first so the ordering of the update is preserved */
xmlUnsetProp(target, a->name);
xmlSetProp(target, a->name, (pcmkXmlStr) p_value);
}
}
for (a_child = pcmk__xml_first_child(update); a_child != NULL;
a_child = pcmk__xml_next(a_child)) {
crm_trace("Updating child <%s%s%s%s%s/>",
pcmk__s(object_name, ""),
object_href ? " " : "",
object_href ? object_href : "",
object_href ? "=" : "",
object_href ? object_href_val : "");
pcmk__xml_update(target, NULL, a_child, as_diff);
}
crm_trace("Finished with <%s%s%s%s%s/>", pcmk__s(object_name, ""),
object_href ? " " : "",
object_href ? object_href : "",
object_href ? "=" : "",
object_href ? object_href_val : "");
}
/*!
* \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;
if (!pcmk__xe_is(search, (const char *) xml->name)) {
// No match: either not both elements, or different element types
return true;
}
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;
}
}
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
pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
{
// See @COMPAT comment in pcmk__xe_replace_match()
CRM_CHECK((xml != NULL) && (search != 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, delete_xe_if_matching, search)) {
// Found and deleted an element
return pcmk_rc_ok;
}
}
// No match found in this subtree
return ENXIO;
}
/*!
* \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);
// May be unnecessary but avoids slight changes to some test outputs
reset_xml_node_flags(new);
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);
}
/*!
* \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)
{
xmlNode *replace = user_data;
const char *xml_id = NULL;
const char *replace_id = NULL;
xml_id = pcmk__xe_id(xml);
replace_id = pcmk__xe_id(replace);
if (!pcmk__xe_is(replace, (const char *) xml->name)) {
// No match: either not both elements, or different element types
return true;
}
if ((replace_id != NULL)
&& !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
// No match: ID was provided in replace and doesn't match xml's ID
return true;
}
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;
}
}
// No match found in this subtree
return ENXIO;
}
/*!
* \internal
* \brief Update one XML subtree with another if the two match
*
* "Update" means to make a target subtree match a source subtree in children
* and attributes, recursively. \c "++" and \c "+=" in attribute values are
* expanded where appropriate (see \c expand_plus_plus()).
*
* A match is defined as follows:
* * \p xml and \p user_data are both element nodes of the same type.
* * \p xml and \p user_data 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 upon match
* \param[in] user_data XML to update \p xml with upon match
*
* \return \c true to continue traversing the tree, or \c false to stop (because
* \p xml was updated by \p user_data)
*
* \note This is compatible with \c pcmk__xml_tree_foreach().
*/
static bool
update_xe_if_matching(xmlNode *xml, void *user_data)
{
xmlNode *update = user_data;
if (!pcmk__xe_is(update, (const char *) xml->name)) {
// No match: either not both elements, or different element types
return true;
}
if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
// No match: ID mismatch
return true;
}
crm_log_xml_trace(xml, "update-match");
crm_log_xml_trace(update, "update-with");
pcmk__xml_update(NULL, xml, update, false);
// Found a match and replaced it; stop traversing tree
return false;
}
/*!
* \internal
* \brief Search an XML tree depth-first and update the first matching element
*
* "Update" means to make a target subtree match a source subtree in children
* and attributes, recursively. \c "++" and \c "+=" in attribute values are
* expanded where appropriate (see \c expand_plus_plus()).
*
* 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 user_data 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
*
* \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)
{
/* @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.
*/
CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, update)) {
// Found and updated an element
return pcmk_rc_ok;
}
// No match found in this subtree
return ENXIO;
}
xmlNode *
sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
{
xmlNode *child = NULL;
GSList *nvpairs = NULL;
xmlNode *result = NULL;
CRM_CHECK(input != NULL, return NULL);
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__xe_first_child(input, NULL, NULL, NULL); child != NULL;
child = pcmk__xe_next(child)) {
if (recursive) {
sorted_xml(child, result, recursive);
} else {
pcmk__xml_copy(result, child);
}
}
return result;
}
/*!
* \internal
* \brief Get next sibling XML element with the same name as a given element
*
* \param[in] node XML element to start from
*
* \return Next sibling XML element with same name
*/
xmlNode *
pcmk__xe_next_same(const xmlNode *node)
{
for (xmlNode *match = pcmk__xe_next(node); match != NULL;
match = pcmk__xe_next(match)) {
if (pcmk__xe_is(match, (const char *) node->name)) {
return match;
}
}
return NULL;
}
void
crm_xml_init(void)
{
static bool init = true;
if(init) {
init = false;
/* The default allocator XML_BUFFER_ALLOC_EXACT does far too many
* pcmk__realloc()s and it can take upwards of 18 seconds (yes, seconds)
* to dump a 28kb tree which XML_BUFFER_ALLOC_DOUBLEIT can do in
* less than 1 second.
*/
xmlSetBufferAllocationScheme(XML_BUFFER_ALLOC_DOUBLEIT);
/* Populate and free the _private field when nodes are created and destroyed */
xmlDeregisterNodeDefault(free_private_data);
xmlRegisterNodeDefault(new_private_data);
crm_schema_init();
}
}
void
crm_xml_cleanup(void)
{
crm_schema_cleanup();
xmlCleanupParser();
}
#define XPATH_MAX 512
xmlNode *
expand_idref(xmlNode * input, xmlNode * top)
{
char *xpath = NULL;
const char *ref = NULL;
xmlNode *result = NULL;
if (input == NULL) {
return NULL;
}
ref = crm_element_value(input, PCMK_XA_ID_REF);
if (ref == NULL) {
return input;
}
if (top == NULL) {
top = input;
}
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;
}
char *
pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
{
static const char *base = NULL;
char *ret = NULL;
if (base == NULL) {
base = pcmk__env_option(PCMK__ENV_SCHEMA_DIRECTORY);
}
if (pcmk__str_empty(base)) {
base = CRM_SCHEMA_DIRECTORY;
}
switch (ns) {
case pcmk__xml_artefact_ns_legacy_rng:
case pcmk__xml_artefact_ns_legacy_xslt:
ret = strdup(base);
break;
case pcmk__xml_artefact_ns_base_rng:
case pcmk__xml_artefact_ns_base_xslt:
ret = crm_strdup_printf("%s/base", base);
break;
default:
crm_err("XML artefact family specified as %u not recognized", ns);
}
return ret;
}
static char *
find_artefact(enum pcmk__xml_artefact_ns ns, const char *path, const char *filespec)
{
char *ret = NULL;
switch (ns) {
case pcmk__xml_artefact_ns_legacy_rng:
case pcmk__xml_artefact_ns_base_rng:
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:
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();
ret = find_artefact(ns, remote_schema_dir, filespec);
}
return ret;
}
void
pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
{
while (true) {
const char *name, *value;
name = va_arg(pairs, const char *);
if (name == NULL) {
return;
}
value = va_arg(pairs, const char *);
if (value != NULL) {
crm_xml_add(node, name, value);
}
}
}
void
pcmk__xe_set_props(xmlNodePtr node, ...)
{
va_list pairs;
va_start(pairs, node);
pcmk__xe_set_propv(node, pairs);
va_end(pairs);
}
int
pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
int (*handler)(xmlNode *xml, void *userdata),
void *userdata)
{
xmlNode *children = (xml? xml->children : NULL);
CRM_ASSERT(handler != NULL);
for (xmlNode *node = children; node != NULL; node = node->next) {
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) {
return rc;
}
}
}
return pcmk_rc_ok;
}
// Deprecated functions kept only for backward API compatibility
// LCOV_EXCL_START
#include
xmlNode *
find_entity(xmlNode *parent, const char *node_name, const char *id)
{
return pcmk__xe_first_child(parent, node_name,
((id == NULL)? id : PCMK_XA_ID), id);
}
void
crm_destroy_xml(gpointer data)
{
free_xml(data);
}
xmlDoc *
getDocPtr(xmlNode *node)
{
xmlDoc *doc = NULL;
CRM_CHECK(node != NULL, return NULL);
doc = node->doc;
if (doc == NULL) {
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__mark_xml_created(child);
return child;
}
int
add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
{
add_node_copy(parent, child);
free_xml(child);
return 1;
}
gboolean
xml_has_children(const xmlNode * xml_root)
{
if (xml_root != NULL && xml_root->children != NULL) {
return TRUE;
}
return FALSE;
}
static char *
replace_text(char *text, size_t *index, size_t *length, const char *replace)
{
// We have space for 1 char already
size_t offset = strlen(replace) - 1;
if (offset > 0) {
*length += offset;
text = pcmk__realloc(text, *length + 1);
// Shift characters to the right to make room for the replacement string
for (size_t i = *length; i > (*index + offset); i--) {
text[i] = text[i - offset];
}
}
// Replace the character at index by the replacement string
memcpy(text + *index, replace, offset + 1);
// Reset index to the end of replacement string
*index += offset;
return text;
}
char *
crm_xml_escape(const char *text)
{
size_t length = 0;
char *copy = NULL;
if (text == NULL) {
return NULL;
}
length = strlen(text);
copy = pcmk__str_copy(text);
for (size_t index = 0; index <= length; index++) {
if(copy[index] & 0x80 && copy[index+1] & 0x80){
index++;
continue;
}
switch (copy[index]) {
case 0:
// Sanity only; loop should stop at the last non-null byte
break;
case '<':
copy = replace_text(copy, &index, &length, "<");
break;
case '>':
copy = replace_text(copy, &index, &length, ">");
break;
case '"':
copy = replace_text(copy, &index, &length, """);
break;
case '\'':
copy = replace_text(copy, &index, &length, "'");
break;
case '&':
copy = replace_text(copy, &index, &length, "&");
break;
case '\t':
/* Might as well just expand to a few spaces... */
copy = replace_text(copy, &index, &length, " ");
break;
case '\n':
copy = replace_text(copy, &index, &length, "\\n");
break;
case '\r':
copy = replace_text(copy, &index, &length, "\\r");
break;
default:
/* Check for and replace non-printing characters with their octal equivalent */
if(copy[index] < ' ' || copy[index] > '~') {
char *replace = crm_strdup_printf("\\%.3o", copy[index]);
copy = replace_text(copy, &index, &length, replace);
free(replace);
}
}
}
return copy;
}
xmlNode *
copy_xml(xmlNode *src)
{
xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
xmlNode *copy = NULL;
pcmk__mem_assert(doc);
copy = xmlDocCopyNode(src, doc, 1);
pcmk__mem_assert(copy);
xmlDocSetRootElement(doc, copy);
return copy;
}
xmlNode *
create_xml_node(xmlNode *parent, const char *name)
{
// Like pcmk__xe_create(), but returns NULL on failure
xmlNode *node = NULL;
CRM_CHECK(!pcmk__str_empty(name), return NULL);
if (parent == NULL) {
xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
if (doc == NULL) {
return NULL;
}
node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
if (node == NULL) {
xmlFreeDoc(doc);
return NULL;
}
xmlDocSetRootElement(doc, node);
} else {
node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
if (node == NULL) {
return NULL;
}
}
pcmk__mark_xml_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 ");
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 : ""));
}
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_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);
+ }
+}
+
// LCOV_EXCL_STOP
// End deprecated API
diff --git a/lib/pacemaker/libpacemaker_private.h b/lib/pacemaker/libpacemaker_private.h
index 88f0355f9a..4738e47609 100644
--- a/lib/pacemaker/libpacemaker_private.h
+++ b/lib/pacemaker/libpacemaker_private.h
@@ -1,1181 +1,1181 @@
/*
* 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 Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMK__LIBPACEMAKER_PRIVATE__H
# define PCMK__LIBPACEMAKER_PRIVATE__H
/* This header is for the sole use of libpacemaker, so that functions can be
* declared with G_GNUC_INTERNAL for efficiency.
*/
#include // lrmd_event_data_t
#include // pcmk_action_t, pcmk_node_t, etc.
#include // pcmk__location_t
// Colocation flags
enum pcmk__coloc_flags {
pcmk__coloc_none = 0U,
// Primary is affected even if already active
pcmk__coloc_influence = (1U << 0),
// Colocation was explicitly configured in CIB
pcmk__coloc_explicit = (1U << 1),
};
// Flags to modify the behavior of add_colocated_node_scores()
enum pcmk__coloc_select {
// With no other flags, apply all "with this" colocations
pcmk__coloc_select_default = 0,
// Apply "this with" colocations instead of "with this" colocations
pcmk__coloc_select_this_with = (1 << 0),
// Apply only colocations with non-negative scores
pcmk__coloc_select_nonnegative = (1 << 1),
// Apply only colocations with at least one matching node
pcmk__coloc_select_active = (1 << 2),
};
// Flags the update_ordered_actions() method can return
enum pcmk__updated {
pcmk__updated_none = 0, // Nothing changed
pcmk__updated_first = (1 << 0), // First action was updated
pcmk__updated_then = (1 << 1), // Then action was updated
};
#define pcmk__set_updated_flags(au_flags, action, flags_to_set) do { \
au_flags = pcmk__set_flags_as(__func__, __LINE__, \
LOG_TRACE, "Action update", \
(action)->uuid, au_flags, \
(flags_to_set), #flags_to_set); \
} while (0)
#define pcmk__clear_updated_flags(au_flags, action, flags_to_clear) do { \
au_flags = pcmk__clear_flags_as(__func__, __LINE__, \
LOG_TRACE, "Action update", \
(action)->uuid, au_flags, \
(flags_to_clear), #flags_to_clear); \
} while (0)
// Resource assignment methods
struct resource_alloc_functions_s {
/*!
* \internal
* \brief Assign a resource to a node
*
* \param[in,out] rsc Resource to assign to a node
* \param[in] prefer Node to prefer, if all else is equal
* \param[in] stop_if_fail If \c true and \p rsc can't be assigned to a
* node, set next role to stopped and update
* existing actions (if \p rsc is not a
* primitive, this applies to its primitive
* descendants instead)
*
* \return Node that \p rsc is assigned to, if assigned entirely to one node
*
* \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource()
* can completely undo the assignment. A successful assignment can be
* either undone or left alone as final. A failed assignment has the
* same effect as calling pcmk__unassign_resource(); there are no side
* effects on roles or actions.
*/
pcmk_node_t *(*assign)(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
bool stop_if_fail);
/*!
* \internal
* \brief Create all actions needed for a given resource
*
* \param[in,out] rsc Resource to create actions for
*/
void (*create_actions)(pcmk_resource_t *rsc);
/*!
* \internal
* \brief Schedule any probes needed for a resource on a node
*
* \param[in,out] rsc Resource to create probe for
* \param[in,out] node Node to create probe on
*
* \return true if any probe was created, otherwise false
*/
bool (*create_probe)(pcmk_resource_t *rsc, pcmk_node_t *node);
/*!
* \internal
* \brief Create implicit constraints needed for a resource
*
* \param[in,out] rsc Resource to create implicit constraints for
*/
void (*internal_constraints)(pcmk_resource_t *rsc);
/*!
* \internal
* \brief Apply a colocation's score to node scores or resource priority
*
* Given a colocation constraint, apply its score to the dependent's
* allowed node scores (if we are still placing resources) or priority (if
* we are choosing promotable clone instance roles).
*
* \param[in,out] dependent Dependent resource in colocation
* \param[in] primary Primary resource in colocation
* \param[in] colocation Colocation constraint to apply
* \param[in] for_dependent true if called on behalf of dependent
*/
void (*apply_coloc_score)(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
bool for_dependent);
/*!
* \internal
* \brief Create list of all resources in colocations with a given resource
*
* Given a resource, create a list of all resources involved in mandatory
* colocations with it, whether directly or via chained colocations.
*
* \param[in] rsc Resource to add to colocated list
* \param[in] orig_rsc Resource originally requested
* \param[in,out] colocated_rscs Existing list
*
* \return List of given resource and all resources involved in colocations
*
* \note This function is recursive; top-level callers should pass NULL as
* \p colocated_rscs and \p orig_rsc, and the desired resource as
* \p rsc. The recursive calls will use other values.
*/
GList *(*colocated_resources)(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *colocated_rscs);
/*!
* \internal
* \brief Add colocations affecting a resource as primary to a list
*
* Given a resource being assigned (\p orig_rsc) and a resource somewhere in
* its chain of ancestors (\p rsc, which may be \p orig_rsc), get
* colocations that affect the ancestor as primary and should affect the
* resource, and add them to a given list.
*
* \param[in] rsc Resource whose colocations should be added
* \param[in] orig_rsc Affected resource (\p rsc or a descendant)
* \param[in,out] list List of colocations to add to
*
* \note All arguments should be non-NULL.
* \note The pcmk__with_this_colocations() wrapper should usually be used
* instead of using this method directly.
*/
void (*with_this_colocations)(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
/*!
* \internal
* \brief Add colocations affecting a resource as dependent to a list
*
* Given a resource being assigned (\p orig_rsc) and a resource somewhere in
* its chain of ancestors (\p rsc, which may be \p orig_rsc), get
* colocations that affect the ancestor as dependent and should affect the
* resource, and add them to a given list.
*
*
* \param[in] rsc Resource whose colocations should be added
* \param[in] orig_rsc Affected resource (\p rsc or a descendant)
* \param[in,out] list List of colocations to add to
*
* \note All arguments should be non-NULL.
* \note The pcmk__this_with_colocations() wrapper should usually be used
* instead of using this method directly.
*/
void (*this_with_colocations)(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
/*!
* \internal
* \brief Update nodes with scores of colocated resources' nodes
*
* Given a table of nodes and a resource, update the nodes' scores with the
* scores of the best nodes matching the attribute used for each of the
* resource's relevant colocations.
*
* \param[in,out] source_rsc Resource whose node scores to add
* \param[in] target_rsc Resource on whose behalf to update \p *nodes
* \param[in] log_id Resource ID for logs (if \c NULL, use
* \p source_rsc ID)
* \param[in,out] nodes Nodes to update (set initial contents to
* \c NULL to copy allowed nodes from
* \p source_rsc)
* \param[in] colocation Original colocation constraint (used to get
* configured primary resource's stickiness, and
* to get colocation node attribute; if \c NULL,
* source_rsc's own matching node scores
* will not be added, and \p *nodes must be
* \c NULL as well)
* \param[in] factor Incorporate scores multiplied by this factor
* \param[in] flags Bitmask of enum pcmk__coloc_select values
*
* \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation,
* and the \c pcmk__coloc_select_this_with flag are used together (and
* only by \c cmp_resources()).
* \note The caller remains responsible for freeing \p *nodes.
*/
void (*add_colocated_node_scores)(pcmk_resource_t *source_rsc,
const pcmk_resource_t *target_rsc,
const char *log_id, GHashTable **nodes,
const pcmk__colocation_t *colocation,
float factor, uint32_t flags);
/*!
* \internal
* \brief Apply a location constraint to a resource's allowed node scores
*
* \param[in,out] rsc Resource to apply constraint to
* \param[in,out] location Location constraint to apply
*/
void (*apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location);
/*!
* \internal
* \brief Return action flags for a given resource action
*
* \param[in,out] action Action to get flags for
* \param[in] node If not NULL, limit effects to this node
*
* \return Flags appropriate to \p action on \p node
* \note For primitives, this will be the same as action->flags regardless
* of node. For collective resources, the flags can differ due to
* multiple instances possibly being involved.
*/
uint32_t (*action_flags)(pcmk_action_t *action, const pcmk_node_t *node);
/*!
* \internal
* \brief Update two actions according to an ordering between them
*
* Given information about an ordering of two actions, update the actions'
* flags (and runnable_before members if appropriate) as appropriate for the
* ordering. Effects may cascade to other orderings involving the actions as
* well.
*
* \param[in,out] first 'First' action in an ordering
* \param[in,out] then 'Then' action in an ordering
* \param[in] node If not NULL, limit scope of ordering to this
* node (only used when interleaving instances)
* \param[in] flags Action flags for \p first for ordering purposes
* \param[in] filter Action flags to limit scope of certain updates
* (may include pcmk_action_optional to affect
* only mandatory actions and pcmk_action_runnable
* to affect only runnable actions)
* \param[in] type Group of enum pcmk__action_relation_flags
* \param[in,out] scheduler Scheduler data
*
* \return Group of enum pcmk__updated flags indicating what was updated
*/
uint32_t (*update_ordered_actions)(pcmk_action_t *first,
pcmk_action_t *then,
const pcmk_node_t *node, uint32_t flags,
uint32_t filter, uint32_t type,
pcmk_scheduler_t *scheduler);
/*!
* \internal
* \brief Output a summary of scheduled actions for a resource
*
* \param[in,out] rsc Resource to output actions for
*/
void (*output_actions)(pcmk_resource_t *rsc);
/*!
* \internal
* \brief Add a resource's actions to the transition graph
*
* \param[in,out] rsc Resource whose actions should be added
*/
void (*add_actions_to_graph)(pcmk_resource_t *rsc);
/*!
* \internal
* \brief Add meta-attributes relevant to transition graph actions to XML
*
* If a given resource supports variant-specific meta-attributes that are
* needed for transition graph actions, add them to a given XML element.
*
* \param[in] rsc Resource whose meta-attributes should be added
* \param[in,out] xml Transition graph action attributes XML to add to
*/
void (*add_graph_meta)(const pcmk_resource_t *rsc, xmlNode *xml);
/*!
* \internal
* \brief Add a resource's utilization to a table of utilization values
*
* This function is used when summing the utilization of a resource and all
* resources colocated with it, to determine whether a node has sufficient
* capacity. Given a resource and a table of utilization values, it will add
* the resource's utilization to the existing values, if the resource has
* not yet been assigned to a node.
*
* \param[in] rsc Resource with utilization to add
* \param[in] orig_rsc Resource being assigned (for logging only)
* \param[in] all_rscs List of all resources that will be summed
* \param[in,out] utilization Table of utilization values to add to
*/
void (*add_utilization)(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc, GList *all_rscs,
GHashTable *utilization);
/*!
* \internal
* \brief Apply a shutdown lock for a resource, if appropriate
*
* \param[in,out] rsc Resource to check for shutdown lock
*/
void (*shutdown_lock)(pcmk_resource_t *rsc);
};
// Actions (pcmk_sched_actions.c)
G_GNUC_INTERNAL
void pcmk__update_action_for_orderings(pcmk_action_t *action,
pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
uint32_t pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
const pcmk_node_t *node, uint32_t flags,
uint32_t filter, uint32_t type,
pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__log_action(const char *pre_text, const pcmk_action_t *action,
bool details);
G_GNUC_INTERNAL
pcmk_action_t *pcmk__new_cancel_action(pcmk_resource_t *rsc, const char *name,
guint interval_ms,
const pcmk_node_t *node);
G_GNUC_INTERNAL
pcmk_action_t *pcmk__new_shutdown_action(pcmk_node_t *node);
G_GNUC_INTERNAL
bool pcmk__action_locks_rsc_to_node(const pcmk_action_t *action);
G_GNUC_INTERNAL
void pcmk__deduplicate_action_inputs(pcmk_action_t *action);
G_GNUC_INTERNAL
void pcmk__output_actions(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
bool pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node,
const xmlNode *xml_op);
G_GNUC_INTERNAL
void pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler);
// Recurring actions (pcmk_sched_recurring.c)
G_GNUC_INTERNAL
void pcmk__create_recurring_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id,
const char *task, guint interval_ms,
const pcmk_node_t *node, const char *reason);
G_GNUC_INTERNAL
void pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task,
guint interval_ms, pcmk_node_t *node);
G_GNUC_INTERNAL
bool pcmk__action_is_recurring(const pcmk_action_t *action);
// Producing transition graphs (pcmk_graph_producer.c)
G_GNUC_INTERNAL
bool pcmk__graph_has_loop(const pcmk_action_t *init_action,
const pcmk_action_t *action,
pcmk__related_action_t *input);
G_GNUC_INTERNAL
void pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__create_graph(pcmk_scheduler_t *scheduler);
// Fencing (pcmk_sched_fencing.c)
G_GNUC_INTERNAL
void pcmk__order_vs_fence(pcmk_action_t *stonith_op,
pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__order_vs_unfence(const pcmk_resource_t *rsc, pcmk_node_t *node,
pcmk_action_t *action,
enum pcmk__action_relation_flags order);
G_GNUC_INTERNAL
void pcmk__fence_guest(pcmk_node_t *node);
G_GNUC_INTERNAL
bool pcmk__node_unfenced(const pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__order_restart_vs_unfence(gpointer data, gpointer user_data);
// Injected scheduler inputs (pcmk_sched_injections.c)
void pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib,
const pcmk_injections_t *injections);
// Constraints of any type (pcmk_sched_constraints.c)
G_GNUC_INTERNAL
pcmk_resource_t *pcmk__find_constraint_resource(GList *rsc_list,
const char *id);
G_GNUC_INTERNAL
xmlNode *pcmk__expand_tags_in_sets(xmlNode *xml_obj,
const pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler,
const char *id, pcmk_resource_t **rsc,
pcmk_tag_t **tag);
G_GNUC_INTERNAL
bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
bool convert_rsc, const pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler);
// Location constraints
G_GNUC_INTERNAL
void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
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, pcmk__location_t *constraint);
// Colocation constraints (pcmk_sched_colocation.c)
enum pcmk__coloc_affects {
pcmk__coloc_affects_nothing = 0,
pcmk__coloc_affects_location,
pcmk__coloc_affects_role,
};
/*!
* \internal
* \brief Get the value of a colocation's node attribute
*
* \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
*
* \return Value of \p attr on \p node or on the host of \p node, as appropriate
*/
static inline const char *
pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr,
const pcmk_resource_t *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;
} 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
enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t
*dependent,
const pcmk_resource_t
*primary,
const pcmk__colocation_t
*colocation,
bool preview);
G_GNUC_INTERNAL
void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation);
G_GNUC_INTERNAL
void pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation);
G_GNUC_INTERNAL
void pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc,
const pcmk_resource_t *target_rsc,
const char *log_id, GHashTable **nodes,
const pcmk__colocation_t *colocation,
float factor, uint32_t flags);
G_GNUC_INTERNAL
void pcmk__add_dependent_scores(gpointer data, gpointer user_data);
G_GNUC_INTERNAL
void pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
const GList *primary_nodes,
bool merge_scores);
G_GNUC_INTERNAL
void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__add_this_with_list(GList **list, GList *addition,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__add_with_this_list(GList **list, GList *addition,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
GList *pcmk__with_this_colocations(const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
GList *pcmk__this_with_colocations(const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__new_colocation(const char *id, const char *node_attr, int score,
pcmk_resource_t *dependent, pcmk_resource_t *primary,
const char *dependent_role, const char *primary_role,
uint32_t flags);
G_GNUC_INTERNAL
void pcmk__block_colocation_dependents(pcmk_action_t *action);
/*!
* \internal
* \brief Check whether colocation's dependent preferences should be considered
*
* \param[in] colocation Colocation constraint
* \param[in] rsc Primary instance (normally this will be
* colocation->primary, which NULL will be treated as,
* but for clones or bundles with multiple instances
* this can be a particular instance)
*
* \return true if colocation influence should be effective, otherwise false
*/
static inline bool
pcmk__colocation_has_influence(const pcmk__colocation_t *colocation,
const pcmk_resource_t *rsc)
{
if (rsc == NULL) {
rsc = colocation->primary;
}
/* A bundle replica colocates its remote connection with its container,
* using a finite score so that the container can run on Pacemaker Remote
* nodes.
*
* Moving a connection is lightweight and does not interrupt the service,
* while moving a container is heavyweight and does interrupt the service,
* so don't move a clean, active container based solely on the preferences
* of its connection.
*
* This also avoids problematic scenarios where two containers want to
* perpetually swap places.
*/
if (pcmk_is_set(colocation->dependent->flags,
pcmk_rsc_remote_nesting_allowed)
&& !pcmk_is_set(rsc->flags, pcmk_rsc_failed)
&& pcmk__list_of_1(rsc->running_on)) {
return false;
}
/* The dependent in a colocation influences the primary's location
* 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);
}
// Ordering constraints (pcmk_sched_ordering.c)
G_GNUC_INTERNAL
void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_task,
pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
char *then_task, pcmk_action_t *then_action,
uint32_t flags, pcmk_scheduler_t *sched);
G_GNUC_INTERNAL
void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__disable_invalid_orderings(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
void pcmk__order_stops_before_shutdown(pcmk_node_t *node,
pcmk_action_t *shutdown_op);
G_GNUC_INTERNAL
void pcmk__apply_orderings(pcmk_scheduler_t *sched);
G_GNUC_INTERNAL
void pcmk__order_after_each(pcmk_action_t *after, GList *list);
/*!
* \internal
* \brief Create a new ordering between two resource actions
*
* \param[in,out] first_rsc Resource for 'first' action
* \param[in,out] first_task Action key for 'first' action
* \param[in] then_rsc Resource for 'then' action
* \param[in,out] then_task Action key for 'then' action
* \param[in] flags Group of enum pcmk__action_relation_flags
*/
#define pcmk__order_resource_actions(first_rsc, first_task, \
then_rsc, then_task, flags) \
pcmk__new_ordering((first_rsc), \
pcmk__op_key((first_rsc)->id, (first_task), 0), \
NULL, \
(then_rsc), \
pcmk__op_key((then_rsc)->id, (then_task), 0), \
NULL, (flags), (first_rsc)->cluster)
#define pcmk__order_starts(rsc1, rsc2, flags) \
pcmk__order_resource_actions((rsc1), PCMK_ACTION_START, \
(rsc2), PCMK_ACTION_START, (flags))
#define pcmk__order_stops(rsc1, rsc2, flags) \
pcmk__order_resource_actions((rsc1), PCMK_ACTION_STOP, \
(rsc2), PCMK_ACTION_STOP, (flags))
// Ticket constraints (pcmk_sched_tickets.c)
G_GNUC_INTERNAL
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler);
// Promotable clone resources (pcmk_sched_promotable.c)
G_GNUC_INTERNAL
void pcmk__add_promotion_scores(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__require_promotion_tickets(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__set_instance_roles(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__create_promotable_actions(pcmk_resource_t *clone);
G_GNUC_INTERNAL
void pcmk__promotable_restart_ordering(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__order_promotable_instances(pcmk_resource_t *clone);
G_GNUC_INTERNAL
void pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary,
pcmk_resource_t *dependent,
const pcmk__colocation_t
*colocation);
G_GNUC_INTERNAL
void pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary,
pcmk_resource_t *dependent,
const pcmk__colocation_t
*colocation);
// Pacemaker Remote nodes (pcmk_sched_remote.c)
G_GNUC_INTERNAL
bool pcmk__is_failed_remote_node(const pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__order_remote_connection_actions(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
bool pcmk__rsc_corresponds_to_guest(const pcmk_resource_t *rsc,
const pcmk_node_t *node);
G_GNUC_INTERNAL
pcmk_node_t *pcmk__connection_host_for_action(const pcmk_action_t *action);
G_GNUC_INTERNAL
void pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params);
G_GNUC_INTERNAL
void pcmk__add_guest_meta_to_xml(xmlNode *args_xml,
const pcmk_action_t *action);
// Primitives (pcmk_sched_primitive.c)
G_GNUC_INTERNAL
pcmk_node_t *pcmk__primitive_assign(pcmk_resource_t *rsc,
const pcmk_node_t *prefer,
bool stop_if_fail);
G_GNUC_INTERNAL
void pcmk__primitive_create_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__primitive_internal_constraints(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
uint32_t pcmk__primitive_action_flags(pcmk_action_t *action,
const pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__primitive_apply_coloc_score(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
bool for_dependent);
G_GNUC_INTERNAL
void pcmk__with_primitive_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__primitive_with_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node,
bool optional);
G_GNUC_INTERNAL
void pcmk__primitive_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml);
G_GNUC_INTERNAL
void pcmk__primitive_add_utilization(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *all_rscs, GHashTable *utilization);
G_GNUC_INTERNAL
void pcmk__primitive_shutdown_lock(pcmk_resource_t *rsc);
// Groups (pcmk_sched_group.c)
G_GNUC_INTERNAL
pcmk_node_t *pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
bool stop_if_fail);
G_GNUC_INTERNAL
void pcmk__group_create_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__group_internal_constraints(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__group_apply_coloc_score(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
bool for_dependent);
G_GNUC_INTERNAL
void pcmk__with_group_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__group_with_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc,
const pcmk_resource_t *target_rsc,
const char *log_id,
GHashTable **nodes,
const pcmk__colocation_t *colocation,
float factor, uint32_t flags);
G_GNUC_INTERNAL
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,
const pcmk_node_t *node);
G_GNUC_INTERNAL
uint32_t pcmk__group_update_ordered_actions(pcmk_action_t *first,
pcmk_action_t *then,
const pcmk_node_t *node,
uint32_t flags, uint32_t filter,
uint32_t type,
pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
GList *pcmk__group_colocated_resources(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *colocated_rscs);
G_GNUC_INTERNAL
void pcmk__group_add_utilization(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *all_rscs, GHashTable *utilization);
G_GNUC_INTERNAL
void pcmk__group_shutdown_lock(pcmk_resource_t *rsc);
// Clones (pcmk_sched_clone.c)
G_GNUC_INTERNAL
pcmk_node_t *pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
bool stop_if_fail);
G_GNUC_INTERNAL
void pcmk__clone_create_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
bool pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__clone_internal_constraints(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
bool for_dependent);
G_GNUC_INTERNAL
void pcmk__with_clone_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__clone_with_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__clone_apply_location(pcmk_resource_t *rsc,
pcmk__location_t *constraint);
G_GNUC_INTERNAL
uint32_t pcmk__clone_action_flags(pcmk_action_t *action,
const pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml);
G_GNUC_INTERNAL
void pcmk__clone_add_utilization(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *all_rscs, GHashTable *utilization);
G_GNUC_INTERNAL
void pcmk__clone_shutdown_lock(pcmk_resource_t *rsc);
// Bundles (pcmk_sched_bundle.c)
G_GNUC_INTERNAL
pcmk_node_t *pcmk__bundle_assign(pcmk_resource_t *rsc,
const pcmk_node_t *prefer, bool stop_if_fail);
G_GNUC_INTERNAL
void pcmk__bundle_create_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
bool pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__bundle_internal_constraints(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent,
const pcmk_resource_t *primary,
const pcmk__colocation_t *colocation,
bool for_dependent);
G_GNUC_INTERNAL
void pcmk__with_bundle_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__bundle_with_colocations(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList **list);
G_GNUC_INTERNAL
void pcmk__bundle_apply_location(pcmk_resource_t *rsc,
pcmk__location_t *constraint);
G_GNUC_INTERNAL
uint32_t pcmk__bundle_action_flags(pcmk_action_t *action,
const pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__output_bundle_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__bundle_add_utilization(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *all_rscs, GHashTable *utilization);
G_GNUC_INTERNAL
void pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc);
// Clone instances or bundle replica containers (pcmk_sched_instances.c)
G_GNUC_INTERNAL
void pcmk__assign_instances(pcmk_resource_t *collective, GList *instances,
int max_total, int max_per_node);
G_GNUC_INTERNAL
void pcmk__create_instance_actions(pcmk_resource_t *rsc, GList *instances);
G_GNUC_INTERNAL
bool pcmk__instance_matches(const pcmk_resource_t *instance,
const pcmk_node_t *node, enum rsc_role_e role,
bool current);
G_GNUC_INTERNAL
pcmk_resource_t *pcmk__find_compatible_instance(const pcmk_resource_t *match_rsc,
const pcmk_resource_t *rsc,
enum rsc_role_e role,
bool current);
G_GNUC_INTERNAL
uint32_t pcmk__instance_update_ordered_actions(pcmk_action_t *first,
pcmk_action_t *then,
const pcmk_node_t *node,
uint32_t flags, uint32_t filter,
uint32_t type,
pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
uint32_t pcmk__collective_action_flags(pcmk_action_t *action,
const GList *instances,
const pcmk_node_t *node);
// Injections (pcmk_injections.c)
G_GNUC_INTERNAL
xmlNode *pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid);
G_GNUC_INTERNAL
xmlNode *pcmk__inject_node_state_change(cib_t *cib_conn, const char *node,
bool up);
G_GNUC_INTERNAL
xmlNode *pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node,
const char *resource,
const char *lrm_name,
const char *rclass,
const char *rtype,
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,
lrmd_event_data_t *op, int target_rc);
// Nodes (pcmk_sched_nodes.c)
G_GNUC_INTERNAL
bool pcmk__node_available(const pcmk_node_t *node, bool consider_score,
bool consider_guest);
G_GNUC_INTERNAL
bool pcmk__any_node_available(GHashTable *nodes);
G_GNUC_INTERNAL
GHashTable *pcmk__copy_node_table(GHashTable *nodes);
G_GNUC_INTERNAL
void pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy);
G_GNUC_INTERNAL
void pcmk__restore_node_tables(pcmk_resource_t *rsc, GHashTable *backup);
G_GNUC_INTERNAL
GList *pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node);
G_GNUC_INTERNAL
void pcmk__apply_node_health(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
pcmk_node_t *pcmk__top_allowed_node(const pcmk_resource_t *rsc,
const pcmk_node_t *node);
// Functions applying to more than one variant (pcmk_sched_resource.c)
G_GNUC_INTERNAL
void pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
bool pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node,
const xmlNode *rsc_entry, bool active_on_node);
G_GNUC_INTERNAL
GList *pcmk__rscs_matching_id(const char *id,
const pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
GList *pcmk__colocated_resources(const pcmk_resource_t *rsc,
const pcmk_resource_t *orig_rsc,
GList *colocated_rscs);
G_GNUC_INTERNAL
void pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml);
G_GNUC_INTERNAL
void pcmk__output_resource_actions(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
bool pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
bool stop_if_fail);
G_GNUC_INTERNAL
void pcmk__unassign_resource(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
bool pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
pcmk_resource_t **failed);
G_GNUC_INTERNAL
void pcmk__sort_resources(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
gint pcmk__cmp_instance(gconstpointer a, gconstpointer b);
G_GNUC_INTERNAL
gint pcmk__cmp_instance_number(gconstpointer a, gconstpointer b);
// Functions related to probes (pcmk_sched_probes.c)
G_GNUC_INTERNAL
bool pcmk__probe_rsc_on_node(pcmk_resource_t *rsc, pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__order_probes(pcmk_scheduler_t *scheduler);
G_GNUC_INTERNAL
bool pcmk__probe_resource_list(GList *rscs, pcmk_node_t *node);
G_GNUC_INTERNAL
void pcmk__schedule_probes(pcmk_scheduler_t *scheduler);
// Functions related to live migration (pcmk_sched_migration.c)
void pcmk__create_migration_actions(pcmk_resource_t *rsc,
const pcmk_node_t *current);
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(pcmk__action_relation_t *order);
// Functions related to node utilization (pcmk_sched_utilization.c)
G_GNUC_INTERNAL
int pcmk__compare_node_capacities(const pcmk_node_t *node1,
const pcmk_node_t *node2);
G_GNUC_INTERNAL
void pcmk__consume_node_capacity(GHashTable *current_utilization,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__release_node_capacity(GHashTable *current_utilization,
const pcmk_resource_t *rsc);
G_GNUC_INTERNAL
const pcmk_node_t *pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc);
G_GNUC_INTERNAL
void pcmk__create_utilization_constraints(pcmk_resource_t *rsc,
const GList *allowed_nodes);
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_injections.c b/lib/pacemaker/pcmk_injections.c
index 850284aa56..80c9e9aa1b 100644
--- a/lib/pacemaker/pcmk_injections.c
+++ b/lib/pacemaker/pcmk_injections.c
@@ -1,798 +1,819 @@
/*
* Copyright 2009-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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // lrmd_event_data_t, etc.
#include
#include
#include
#include "libpacemaker_private.h"
bool pcmk__simulate_node_config = false;
#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 \
"//" PCMK__XE_LRM_RESOURCE "[@" PCMK_XA_ID "='%s']"
/*!
* \internal
* \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 \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
*/
static void
inject_transient_attr(pcmk__output_t *out, xmlNode *cib_node,
const char *name, const char *value)
{
xmlNode *attrs = NULL;
xmlNode *instance_attrs = NULL;
const char *node_uuid = pcmk__xe_id(cib_node);
out->message(out, "inject-attr", name, value, cib_node);
attrs = pcmk__xe_first_child(cib_node, PCMK__XE_TRANSIENT_ATTRIBUTES, NULL,
NULL);
if (attrs == NULL) {
attrs = pcmk__xe_create(cib_node, PCMK__XE_TRANSIENT_ATTRIBUTES);
crm_xml_add(attrs, PCMK_XA_ID, node_uuid);
}
instance_attrs = pcmk__xe_first_child(attrs, PCMK_XE_INSTANCE_ATTRIBUTES,
NULL, NULL);
if (instance_attrs == NULL) {
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);
}
/*!
* \internal
* \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);
}
/*!
* \internal
* \brief Create a CIB configuration entry for a fictitious node
*
* \param[in,out] cib_conn CIB object to use
* \param[in] node Node name to use
*/
static void
create_node_entry(cib_t *cib_conn, const char *node)
{
int rc = pcmk_ok;
char *xpath = crm_strdup_printf(XPATH_NODE_CONFIG, node);
rc = cib_conn->cmds->query(cib_conn, xpath, NULL,
cib_xpath|cib_sync_call|cib_scope_local);
if (rc == -ENXIO) { // Only add if not already existing
xmlNode *cib_object = pcmk__xe_create(NULL, PCMK_XE_NODE);
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()... */
free_xml(cib_object);
}
free(xpath);
}
/*!
* \internal
* \brief Synthesize a fake executor event for an action
*
* \param[in] cib_resource XML for any existing resource action history
* \param[in] task Name of action to synthesize
* \param[in] interval_ms Interval of action to synthesize
* \param[in] outcome Result of action to synthesize
*
* \return Newly allocated executor event
* \note It is the caller's responsibility to free the result with
* lrmd_free_event().
*/
static lrmd_event_data_t *
create_op(const xmlNode *cib_resource, const char *task, guint interval_ms,
int outcome)
{
lrmd_event_data_t *op = NULL;
xmlNode *xop = NULL;
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);
op->t_rcchange = op->t_run;
// Use a call ID higher than any existing history entries
op->call_id = 0;
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, PCMK__XA_CALL_ID, &tmp);
if (tmp > op->call_id) {
op->call_id = tmp;
}
}
op->call_id++;
return op;
}
/*!
* \internal
* \brief Inject a fictitious resource history entry into a scheduler input
*
* \param[in,out] cib_resource Resource history XML to inject entry into
* \param[in,out] op Action result to inject
* \param[in] target_rc Expected result for action to inject
*
* \return XML of injected resource history entry
*/
xmlNode *
pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op,
int target_rc)
{
return pcmk__create_history_xml(cib_resource, op, CRM_FEATURE_SET,
target_rc, NULL, crm_system_name);
}
/*!
* \internal
* \brief Inject a fictitious node into a scheduler input
*
* \param[in,out] cib_conn Scheduler input CIB to inject node into
* \param[in] node Name of node to inject
* \param[in] uuid UUID of node to inject
*
* \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.
*/
xmlNode *
pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
{
int rc = pcmk_ok;
xmlNode *cib_object = NULL;
char *xpath = crm_strdup_printf(XPATH_NODE_STATE, node);
bool duplicate = false;
char *found_uuid = NULL;
if (pcmk__simulate_node_config) {
create_node_entry(cib_conn, node);
}
rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
cib_xpath|cib_sync_call|cib_scope_local);
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;
}
if (rc == -ENXIO) {
if (uuid == NULL) {
query_node_uuid(cib_conn, node, &found_uuid, NULL);
} else {
found_uuid = strdup(uuid);
}
if (found_uuid) {
char *xpath_by_uuid = crm_strdup_printf(XPATH_NODE_STATE_BY_ID,
found_uuid);
/* 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) && (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;
free(xpath_by_uuid);
goto done;
} else if (cib_object != NULL) {
crm_xml_add(cib_object, PCMK_XA_UNAME, node);
rc = cib_conn->cmds->modify(cib_conn, PCMK_XE_STATUS,
cib_object,
cib_sync_call|cib_scope_local);
}
free(xpath_by_uuid);
}
}
if (rc == -ENXIO) {
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);
rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object,
cib_xpath|cib_sync_call|cib_scope_local);
crm_trace("Injecting node state for %s (rc=%d)", node, rc);
}
done:
free(found_uuid);
free(xpath);
if (duplicate) {
crm_log_xml_warn(cib_object, "Duplicates");
crm_exit(CRM_EX_SOFTWARE);
return NULL; // not reached, but makes static analysis happy
}
CRM_ASSERT(rc == pcmk_ok);
return cib_object;
}
/*!
* \internal
* \brief Inject a fictitious node state change into a scheduler input
*
* \param[in,out] cib_conn Scheduler input CIB to inject into
* \param[in] node Name of node to inject change for
* \param[in] up If true, change state to online, otherwise offline
*
* \return XML of changed (or added) node state entry
*/
xmlNode *
pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
{
xmlNode *cib_node = pcmk__inject_node(cib_conn, node, NULL);
if (up) {
pcmk__xe_set_props(cib_node,
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,
NULL);
} else {
pcmk__xe_set_props(cib_node,
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,
NULL);
}
crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, crm_system_name);
return cib_node;
}
/*!
* \internal
* \brief Check whether a node has history for a given resource
*
* \param[in,out] cib_node Node state XML to check
* \param[in] resource Resource name to check for
*
* \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, PCMK_XA_UNAME);
char *xpath = crm_strdup_printf(XPATH_RSC_HISTORY, node, resource);
xmlNode *match = get_xpath_object(xpath, cib_node, LOG_TRACE);
free(xpath);
return match;
}
/*!
* \internal
* \brief Inject a resource history element into a scheduler input
*
* \param[in,out] out Output object for displaying error messages
* \param[in,out] cib_node Node state XML to inject resource history entry into
* \param[in] resource ID (in configuration) of resource to inject
* \param[in] lrm_name ID as used in history (could be clone instance)
* \param[in] rclass Resource agent class of resource to inject
* \param[in] rtype Resource agent type of resource to inject
* \param[in] rprovider Resource agent provider of resource to inject
*
* \return XML of injected resource history element
* \note If a history element already exists under either \p resource or
* \p lrm_name, this will return it rather than injecting a new one.
*/
xmlNode *
pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node,
const char *resource, const char *lrm_name,
const char *rclass, const char *rtype,
const char *rprovider)
{
xmlNode *lrm = NULL;
xmlNode *container = NULL;
xmlNode *cib_resource = NULL;
cib_resource = find_resource_xml(cib_node, resource);
if (cib_resource != NULL) {
/* If an existing LRM history entry uses the resource name,
* continue using it, even if lrm_name is different.
*/
return cib_resource;
}
// Check for history entry under preferred name
if (strcmp(resource, lrm_name) != 0) {
cib_resource = find_resource_xml(cib_node, lrm_name);
if (cib_resource != NULL) {
return cib_resource;
}
}
if ((rclass == NULL) || (rtype == NULL)) {
// @TODO query configuration for class, provider, type
out->err(out,
"Resource %s not found in the status section of %s "
"(supply class and type to continue)",
resource, pcmk__xe_id(cib_node));
return NULL;
} else if (!pcmk__strcase_any_of(rclass,
PCMK_RESOURCE_CLASS_OCF,
PCMK_RESOURCE_CLASS_STONITH,
PCMK_RESOURCE_CLASS_SERVICE,
PCMK_RESOURCE_CLASS_UPSTART,
PCMK_RESOURCE_CLASS_SYSTEMD,
PCMK_RESOURCE_CLASS_LSB, NULL)) {
out->err(out, "Invalid class for %s: %s", resource, rclass);
return NULL;
} else if (pcmk_is_set(pcmk_get_ra_caps(rclass), pcmk_ra_cap_provider)
&& (rprovider == NULL)) {
// @TODO query configuration for provider
out->err(out, "Please specify the provider for resource %s", resource);
return NULL;
}
crm_info("Injecting new resource %s into node state '%s'",
lrm_name, pcmk__xe_id(cib_node));
lrm = pcmk__xe_first_child(cib_node, PCMK__XE_LRM, NULL, NULL);
if (lrm == NULL) {
const char *node_uuid = pcmk__xe_id(cib_node);
lrm = pcmk__xe_create(cib_node, PCMK__XE_LRM);
crm_xml_add(lrm, PCMK_XA_ID, node_uuid);
}
container = pcmk__xe_first_child(lrm, PCMK__XE_LRM_RESOURCES, NULL, NULL);
if (container == NULL) {
container = pcmk__xe_create(lrm, PCMK__XE_LRM_RESOURCES);
}
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, PCMK_XA_ID, lrm_name);
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,
"/" PCMK_XE_CIB "/" PCMK_XE_STATUS "/" PCMK_XE_TICKETS);
if (ticket_id) {
pcmk__g_strcat(xpath,
"/" PCMK__XE_TICKET_STATE
"[@" PCMK_XA_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 " PCMK__XE_TICKET_STATE "s match ticket_id=%s",
ticket_id);
}
*ticket_state_xml = xml_search;
return rc;
}
/*!
* \internal
* \brief Inject a ticket attribute into ticket state
*
* \param[in,out] out Output object for displaying error messages
* \param[in] ticket_id Ticket whose state should be changed
* \param[in] attr_name Ticket attribute name to inject
* \param[in] attr_value Boolean value of ticket attribute to inject
* \param[in,out] cib CIB object to use
*
* \return Standard Pacemaker return code
*/
static int
set_ticket_state_attr(pcmk__output_t *out, const char *ticket_id,
const char *attr_name, bool attr_value, cib_t *cib)
{
int rc = pcmk_rc_ok;
xmlNode *xml_top = NULL;
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);
if (rc == pcmk_rc_ok) { // Ticket state found, use it
crm_debug("Injecting attribute into existing ticket state %s",
ticket_id);
xml_top = ticket_state_xml;
} else if (rc == ENXIO) { // No ticket state, 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);
} else { // Error
return rc;
}
// Add the attribute to the ticket state
pcmk__xe_set_bool_attr(ticket_state_xml, attr_name, attr_value);
crm_log_xml_debug(xml_top, "Update");
// Commit the change to the CIB
rc = cib->cmds->modify(cib, PCMK_XE_STATUS, xml_top,
cib_sync_call|cib_scope_local);
rc = pcmk_legacy2rc(rc);
free_xml(xml_top);
return rc;
}
/*!
* \internal
* \brief Inject a fictitious action into the cluster
*
* \param[in,out] out Output object for displaying error messages
* \param[in] spec Action specification to inject
* \param[in,out] cib CIB object for scheduler input
* \param[in] scheduler Scheduler data
*/
static void
inject_action(pcmk__output_t *out, const char *spec, cib_t *cib,
const pcmk_scheduler_t *scheduler)
{
int rc;
int outcome = PCMK_OCF_OK;
guint interval_ms = 0;
char *key = NULL;
char *node = NULL;
char *task = NULL;
char *resource = NULL;
const char *rtype = NULL;
const char *rclass = NULL;
const char *rprovider = NULL;
xmlNode *cib_op = NULL;
xmlNode *cib_node = NULL;
xmlNode *cib_resource = NULL;
const pcmk_resource_t *rsc = NULL;
lrmd_event_data_t *op = NULL;
out->message(out, "inject-spec", spec);
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",
spec, rc);
goto done;
}
parse_op_key(key, &resource, &task, &interval_ms);
rsc = pe_find_resource(scheduler->resources, resource);
if (rsc == NULL) {
out->err(out, "Invalid resource name: %s", resource);
goto done;
}
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,
rclass, rtype, rprovider);
CRM_ASSERT(cib_resource != NULL);
op = create_op(cib_resource, task, interval_ms, outcome);
CRM_ASSERT(op != NULL);
cib_op = pcmk__inject_action_result(cib_resource, op, 0);
CRM_ASSERT(cib_op != NULL);
lrmd_free_event(op);
rc = cib->cmds->modify(cib, PCMK_XE_STATUS, cib_node,
cib_sync_call|cib_scope_local);
CRM_ASSERT(rc == pcmk_ok);
done:
free(task);
free(node);
free(key);
}
/*!
* \internal
* \brief Inject fictitious scheduler inputs
*
* \param[in,out] scheduler Scheduler data
* \param[in,out] cib CIB object for scheduler input to modify
* \param[in] injections Injections to apply
*/
void
pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib,
const pcmk_injections_t *injections)
{
int rc = pcmk_ok;
const GList *iter = NULL;
xmlNode *cib_node = NULL;
pcmk__output_t *out = scheduler->priv;
out->message(out, "inject-modify-config", injections->quorum,
injections->watchdog);
if (injections->quorum != NULL) {
xmlNode *top = pcmk__xe_create(NULL, PCMK_XE_CIB);
/* 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);
}
if (injections->watchdog != NULL) {
rc = cib__update_node_attr(out, cib, cib_sync_call|cib_scope_local,
PCMK_XE_CRM_CONFIG, NULL, NULL, NULL,
NULL, PCMK_OPT_HAVE_WATCHDOG,
injections->watchdog, NULL, NULL);
CRM_ASSERT(rc == pcmk_rc_ok);
}
for (iter = injections->node_up; iter != NULL; iter = iter->next) {
const char *node = (const char *) iter->data;
out->message(out, "inject-modify-node", "Online", node);
cib_node = pcmk__inject_node_state_change(cib, node, true);
CRM_ASSERT(cib_node != NULL);
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);
}
for (iter = injections->node_down; iter != NULL; iter = iter->next) {
const char *node = (const char *) iter->data;
char *xpath = NULL;
out->message(out, "inject-modify-node", "Offline", node);
cib_node = pcmk__inject_node_state_change(cib, node, false);
CRM_ASSERT(cib_node != NULL);
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("//" 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("//" 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);
}
for (iter = injections->node_fail; iter != NULL; iter = iter->next) {
const char *node = (const char *) iter->data;
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, PCMK_VALUE_FALSE);
CRM_ASSERT(cib_node != NULL);
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);
}
for (iter = injections->ticket_grant; iter != NULL; iter = iter->next) {
const char *ticket_id = (const char *) iter->data;
out->message(out, "inject-modify-ticket", "Granting", ticket_id);
rc = set_ticket_state_attr(out, ticket_id, PCMK__XA_GRANTED, true, cib);
CRM_ASSERT(rc == pcmk_rc_ok);
}
for (iter = injections->ticket_revoke; iter != NULL; iter = iter->next) {
const char *ticket_id = (const char *) iter->data;
out->message(out, "inject-modify-ticket", "Revoking", ticket_id);
rc = set_ticket_state_attr(out, ticket_id, PCMK__XA_GRANTED, false,
cib);
CRM_ASSERT(rc == pcmk_rc_ok);
}
for (iter = injections->ticket_standby; iter != NULL; iter = iter->next) {
const char *ticket_id = (const char *) iter->data;
out->message(out, "inject-modify-ticket", "Standby", ticket_id);
rc = set_ticket_state_attr(out, ticket_id, PCMK_XA_STANDBY, true, cib);
CRM_ASSERT(rc == pcmk_rc_ok);
}
for (iter = injections->ticket_activate; iter != NULL; iter = iter->next) {
const char *ticket_id = (const char *) iter->data;
out->message(out, "inject-modify-ticket", "Activating", ticket_id);
rc = set_ticket_state_attr(out, ticket_id, PCMK_XA_STANDBY, false, cib);
CRM_ASSERT(rc == pcmk_rc_ok);
}
for (iter = injections->op_inject; iter != NULL; iter = iter->next) {
inject_action(out, (const char *) iter->data, cib, scheduler);
}
if (!out->is_quiet(out)) {
out->end_list(out);
}
}
void
pcmk_free_injections(pcmk_injections_t *injections)
{
if (injections == NULL) {
return;
}
g_list_free_full(injections->node_up, g_free);
g_list_free_full(injections->node_down, g_free);
g_list_free_full(injections->node_fail, g_free);
g_list_free_full(injections->op_fail, g_free);
g_list_free_full(injections->op_inject, g_free);
g_list_free_full(injections->ticket_grant, g_free);
g_list_free_full(injections->ticket_revoke, g_free);
g_list_free_full(injections->ticket_standby, g_free);
g_list_free_full(injections->ticket_activate, g_free);
free(injections->quorum);
free(injections->watchdog);
free(injections);
}
diff --git a/lib/pacemaker/pcmk_simulate.c b/lib/pacemaker/pcmk_simulate.c
index 9aebb84949..88f86fb835 100644
--- a/lib/pacemaker/pcmk_simulate.c
+++ b/lib/pacemaker/pcmk_simulate.c
@@ -1,1013 +1,1013 @@
/*
* 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 Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "libpacemaker_private.h"
static pcmk__output_t *out = NULL;
static cib_t *fake_cib = NULL;
static GList *fake_resource_list = NULL;
static const GList *fake_op_fail_list = NULL;
static void set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
const char *use_date);
/*!
* \internal
* \brief Create an action name for use in a dot graph
*
* \param[in] action Action to create name for
* \param[in] verbose If true, add action ID to name
*
* \return Newly allocated string with action name
* \note It is the caller's responsibility to free the result.
*/
static char *
create_action_name(const pcmk_action_t *action, bool verbose)
{
char *action_name = NULL;
const char *prefix = "";
const char *action_host = NULL;
const char *clone_name = NULL;
const char *task = action->task;
if (action->node != NULL) {
action_host = action->node->details->uname;
} else if (!pcmk_is_set(action->flags, pcmk_action_pseudo)) {
action_host = "";
}
if (pcmk__str_eq(action->task, PCMK_ACTION_CANCEL, pcmk__str_none)) {
prefix = "Cancel ";
task = action->cancel_task;
}
if (action->rsc != NULL) {
clone_name = action->rsc->clone_name;
}
if (clone_name != NULL) {
char *key = NULL;
guint interval_ms = 0;
if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
&interval_ms) != pcmk_rc_ok) {
interval_ms = 0;
}
if (pcmk__strcase_any_of(action->task, PCMK_ACTION_NOTIFY,
PCMK_ACTION_NOTIFIED, NULL)) {
const char *n_type = g_hash_table_lookup(action->meta,
"notify_key_type");
const char *n_task = g_hash_table_lookup(action->meta,
"notify_key_operation");
CRM_ASSERT(n_type != NULL);
CRM_ASSERT(n_task != NULL);
key = pcmk__notify_key(clone_name, n_type, n_task);
} else {
key = pcmk__op_key(clone_name, task, interval_ms);
}
if (action_host != NULL) {
action_name = crm_strdup_printf("%s%s %s",
prefix, key, action_host);
} else {
action_name = crm_strdup_printf("%s%s", prefix, key);
}
free(key);
} else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
pcmk__str_none)) {
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);
} else if (action->rsc && action_host) {
action_name = crm_strdup_printf("%s%s %s",
prefix, action->uuid, action_host);
} else if (action_host) {
action_name = crm_strdup_printf("%s%s %s",
prefix, action->task, action_host);
} else {
action_name = crm_strdup_printf("%s", action->uuid);
}
if (verbose) {
char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
free(action_name);
action_name = with_id;
}
return action_name;
}
/*!
* \internal
* \brief Display the status of a cluster
*
* \param[in,out] scheduler Scheduler data
* \param[in] show_opts How to modify display (as pcmk_show_opt_e flags)
* \param[in] section_opts Sections to display (as pcmk_section_e flags)
* \param[in] title What to use as list title
* \param[in] print_spacer Whether to display a spacer first
*/
static void
print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts,
uint32_t section_opts, const char *title,
bool print_spacer)
{
pcmk__output_t *out = scheduler->priv;
GList *all = NULL;
crm_exit_t stonith_rc = 0;
enum pcmk_pacemakerd_state state = pcmk_pacemakerd_state_invalid;
section_opts |= pcmk_section_nodes | pcmk_section_resources;
show_opts |= pcmk_show_inactive_rscs | pcmk_show_failed_detail;
all = g_list_prepend(all, (gpointer) "*");
PCMK__OUTPUT_SPACER_IF(out, print_spacer);
out->begin_list(out, NULL, NULL, "%s", title);
out->message(out, "cluster-status",
scheduler, state, stonith_rc, NULL,
pcmk__fence_history_none, section_opts, show_opts, NULL,
all, all);
out->end_list(out);
g_list_free(all);
}
/*!
* \internal
* \brief Display a summary of all actions scheduled in a transition
*
* \param[in,out] scheduler Scheduler data (fully scheduled)
* \param[in] print_spacer Whether to display a spacer first
*/
static void
print_transition_summary(pcmk_scheduler_t *scheduler, bool print_spacer)
{
pcmk__output_t *out = scheduler->priv;
PCMK__OUTPUT_SPACER_IF(out, print_spacer);
out->begin_list(out, NULL, NULL, "Transition Summary");
pcmk__output_actions(scheduler);
out->end_list(out);
}
/*!
* \internal
* \brief Reset scheduler input, output, date, and flags
*
* \param[in,out] scheduler Scheduler data
* \param[in] input What to set as cluster input
* \param[in] out What to set as cluster output object
* \param[in] use_date What to set as cluster's current timestamp
* \param[in] flags Group of enum pcmk_scheduler_flags to set
*/
static void
reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out,
const char *use_date, unsigned int flags)
{
scheduler->input = input;
scheduler->priv = out;
set_effective_date(scheduler, true, use_date);
if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
pcmk__set_scheduler_flags(scheduler, pcmk_sched_sanitized);
}
if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores);
}
if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization);
}
}
/*!
* \brief Write out a file in dot(1) format describing the actions that will
* be taken by the scheduler in response to an input CIB file.
*
* \param[in,out] scheduler Scheduler data
* \param[in] dot_file The filename to write
* \param[in] all_actions Write all actions, even those that are optional
* or are on unmanaged resources
* \param[in] verbose Add extra information, such as action IDs, to the
* output
*
* \return Standard Pacemaker return code
*/
static int
write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file,
bool all_actions, bool verbose)
{
GList *iter = NULL;
FILE *dot_strm = fopen(dot_file, "w");
if (dot_strm == NULL) {
return errno;
}
fprintf(dot_strm, " digraph \"g\" {\n");
for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
pcmk_action_t *action = (pcmk_action_t *) iter->data;
const char *style = "dashed";
const char *font = "black";
const char *color = "black";
char *action_name = create_action_name(action, verbose);
if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
font = "orange";
}
if (pcmk_is_set(action->flags, pcmk_action_added_to_graph)) {
style = PCMK__VALUE_BOLD;
color = "green";
} else if ((action->rsc != NULL)
&& !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)) {
color = "red";
font = "purple";
if (!all_actions) {
goto do_not_write;
}
} else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
color = "blue";
if (!all_actions) {
goto do_not_write;
}
} else {
color = "red";
CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pcmk_action_runnable));
}
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:
free(action_name);
}
for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
pcmk_action_t *action = (pcmk_action_t *) iter->data;
for (GList *before_iter = action->actions_before;
before_iter != NULL; before_iter = before_iter->next) {
pcmk__related_action_t *before = before_iter->data;
char *before_name = NULL;
char *after_name = NULL;
const char *style = "dashed";
bool optional = true;
if (before->state == pe_link_dumped) {
optional = false;
style = PCMK__VALUE_BOLD;
} else if ((uint32_t) before->type == pcmk__ar_none) {
continue;
} else if (pcmk_is_set(before->action->flags,
pcmk_action_added_to_graph)
&& pcmk_is_set(action->flags, pcmk_action_added_to_graph)
&& (uint32_t) before->type != pcmk__ar_if_on_same_node_or_target) {
optional = false;
}
if (all_actions || !optional) {
before_name = create_action_name(before->action, verbose);
after_name = create_action_name(action, verbose);
fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
before_name, after_name, style);
free(before_name);
free(after_name);
}
}
}
fprintf(dot_strm, "}\n");
fflush(dot_strm);
fclose(dot_strm);
return pcmk_rc_ok;
}
/*!
* \brief Profile the configuration updates and scheduler actions in a single
* CIB file, printing the profiling timings.
*
* \note \p scheduler->priv must have been set to a valid \p pcmk__output_t
* object before this function is called.
*
* \param[in] xml_file The CIB file to profile
* \param[in] repeat Number of times to run
* \param[in,out] scheduler Scheduler data
* \param[in] use_date The date to set the cluster's time to (may be NULL)
*/
static void
profile_file(const char *xml_file, long long repeat,
pcmk_scheduler_t *scheduler, const char *use_date)
{
pcmk__output_t *out = scheduler->priv;
xmlNode *cib_object = NULL;
clock_t start = 0;
clock_t end;
unsigned long long scheduler_flags = pcmk_sched_no_compat;
CRM_ASSERT(out != NULL);
cib_object = pcmk__xml_read(xml_file);
start = clock();
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) {
free_xml(cib_object);
return;
}
if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
free_xml(cib_object);
return;
}
if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
scheduler_flags |= pcmk_sched_output_scores;
}
if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
scheduler_flags |= pcmk_sched_show_utilization;
}
for (int i = 0; i < repeat; ++i) {
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);
pe_reset_working_set(scheduler);
}
end = clock();
out->message(out, "profile", xml_file, start, end);
}
void
pcmk__profile_dir(const char *dir, long long repeat,
pcmk_scheduler_t *scheduler, const char *use_date)
{
pcmk__output_t *out = scheduler->priv;
struct dirent **namelist;
int file_num = scandir(dir, &namelist, 0, alphasort);
CRM_ASSERT(out != NULL);
if (file_num > 0) {
struct stat prop;
char buffer[FILENAME_MAX];
out->begin_list(out, NULL, NULL, "Timings");
while (file_num--) {
if ('.' == namelist[file_num]->d_name[0]) {
free(namelist[file_num]);
continue;
} else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
".xml")) {
free(namelist[file_num]);
continue;
}
snprintf(buffer, sizeof(buffer), "%s/%s",
dir, namelist[file_num]->d_name);
if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
profile_file(buffer, repeat, scheduler, use_date);
}
free(namelist[file_num]);
}
free(namelist);
out->end_list(out);
}
}
/*!
* \brief Set the date of the cluster, either to the value given by
* \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 \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)
*/
static void
set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
const char *use_date)
{
pcmk__output_t *out = scheduler->priv;
time_t original_date = 0;
CRM_ASSERT(out != NULL);
crm_element_value_epoch(scheduler->input, PCMK_XA_EXECUTION_DATE,
&original_date);
if (use_date) {
scheduler->now = crm_time_new(use_date);
out->info(out, "Setting effective cluster time: %s", use_date);
crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->now,
crm_time_log_date | crm_time_log_timeofday);
} else if (original_date != 0) {
scheduler->now = pcmk__copy_timet(original_date);
if (print_original) {
char *when = crm_time_as_string(scheduler->now,
crm_time_log_date|crm_time_log_timeofday);
out->info(out, "Using the original execution date of: %s", when);
free(when);
}
}
}
/*!
* \internal
* \brief Simulate successfully executing a pseudo-action in a graph
*
* \param[in,out] graph Graph to update with pseudo-action result
* \param[in,out] action Pseudo-action to simulate executing
*
* \return Standard Pacemaker return code
*/
static int
simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
{
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);
pcmk__update_graph(graph, action);
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Simulate executing a resource action in a graph
*
* \param[in,out] graph Graph to update with resource action result
* \param[in,out] action Resource action to simulate executing
*
* \return Standard Pacemaker return code
*/
static int
simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
{
int rc;
lrmd_event_data_t *op = NULL;
int target_outcome = PCMK_OCF_OK;
const char *rtype = NULL;
const char *rclass = NULL;
const char *resource = NULL;
const char *rprovider = NULL;
const char *resource_config_name = NULL;
const char *operation = crm_element_value(action->xml, PCMK_XA_OPERATION);
const char *target_rc_s = crm_meta_value(action->params,
PCMK__META_OP_TARGET_RC);
xmlNode *cib_node = NULL;
xmlNode *cib_resource = NULL;
xmlNode *action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE,
NULL, NULL);
char *node = crm_element_value_copy(action->xml, PCMK__META_ON_NODE);
char *uuid = NULL;
const char *router_node = crm_element_value(action->xml,
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)) {
crm_debug("No history injection for %s op on %s", operation, node);
goto done; // Confirm action and update graph
}
if (action_rsc == NULL) { // Shouldn't be possible
crm_log_xml_err(action->xml, "Bad");
free(node);
return EPROTO;
}
/* A resource might be known by different names in the configuration and in
* the action (for example, a clone instance). Grab the configuration name
* (which is preferred when writing history), and if necessary, the instance
* name.
*/
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);
return EPROTO;
}
resource = resource_config_name;
if (pe_find_resource(fake_resource_list, resource) == NULL) {
const char *longname = crm_element_value(action_rsc, PCMK__XA_LONG_ID);
if ((longname != NULL)
&& (pe_find_resource(fake_resource_list, longname) != NULL)) {
resource = longname;
}
}
// Certain actions need to be displayed but don't need history entries
if (pcmk__strcase_any_of(operation, PCMK_ACTION_DELETE,
PCMK_ACTION_META_DATA, NULL)) {
out->message(out, "inject-rsc-action", resource, operation, node,
(guint) 0);
goto done; // Confirm action and update graph
}
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);
CRM_ASSERT(fake_cib->cmds->query(fake_cib, NULL, NULL,
cib_sync_call|cib_scope_local) == pcmk_ok);
// Ensure the action node is in the CIB
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);
CRM_ASSERT(cib_node != NULL);
// Add a history entry for the action
cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
resource_config_name,
rclass, rtype, rprovider);
if (cib_resource == NULL) {
crm_err("Could not simulate action %d history for resource %s",
action->id, resource);
free(node);
free_xml(cib_node);
return EINVAL;
}
// Simulate and display an executor event for the action result
op = pcmk__event_from_graph_action(cib_resource, action, PCMK_EXEC_DONE,
target_outcome, "User-injected result");
out->message(out, "inject-rsc-action", resource, op->op_type, node,
op->interval_ms);
// Check whether action is in a list of desired simulated failures
for (const GList *iter = fake_op_fail_list;
iter != NULL; iter = iter->next) {
const char *spec = (const char *) iter->data;
char *key = NULL;
const char *match_name = NULL;
// Allow user to specify anonymous clone with or without instance number
key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
op->interval_ms, node);
if (strncasecmp(key, spec, strlen(key)) == 0) {
match_name = resource;
}
free(key);
// If not found, try the resource's name in the configuration
if ((match_name == NULL)
&& (strcmp(resource, resource_config_name) != 0)) {
key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
op->op_type, op->interval_ms, node);
if (strncasecmp(key, spec, strlen(key)) == 0) {
match_name = resource_config_name;
}
free(key);
}
if (match_name == NULL) {
continue; // This failed action entry doesn't match
}
// ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
if (rc != 1) {
out->err(out, "Invalid failed operation '%s' "
"(result code must be integer)", spec);
continue; // Keep checking other list entries
}
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 = PCMK_SCORE_INFINITY;
- pcmk__inject_failcount(out, cib_node, match_name, op->op_type,
+ 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, PCMK_XE_STATUS, cib_node,
cib_sync_call|cib_scope_local);
CRM_ASSERT(rc == pcmk_ok);
done:
free(node);
free_xml(cib_node);
pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
pcmk__update_graph(graph, action);
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Simulate successfully executing a cluster action
*
* \param[in,out] graph Graph to update with action result
* \param[in,out] action Cluster action to simulate
*
* \return Standard Pacemaker return code
*/
static int
simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
{
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);
pcmk__update_graph(graph, action);
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Simulate successfully executing a fencing action
*
* \param[in,out] graph Graph to update with action result
* \param[in,out] action Fencing action to simulate
*
* \return Standard Pacemaker return code
*/
static int
simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
{
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);
if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
int rc = pcmk_ok;
GString *xpath = g_string_sized_new(512);
// Set node state to offline
xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
false);
CRM_ASSERT(cib_node != NULL);
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,
"//" 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,
"//" 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);
free_xml(cib_node);
g_string_free(xpath, TRUE);
}
pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
pcmk__update_graph(graph, action);
free(target);
return pcmk_rc_ok;
}
enum pcmk__graph_status
pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib,
const GList *op_fail_list)
{
pcmk__graph_t *transition = NULL;
enum pcmk__graph_status graph_rc;
pcmk__graph_functions_t simulation_fns = {
simulate_pseudo_action,
simulate_resource_action,
simulate_cluster_action,
simulate_fencing_action,
};
out = scheduler->priv;
fake_cib = cib;
fake_op_fail_list = op_fail_list;
if (!out->is_quiet(out)) {
out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
}
pcmk__set_graph_functions(&simulation_fns);
transition = pcmk__unpack_graph(scheduler->graph, crm_system_name);
pcmk__log_graph(LOG_DEBUG, transition);
fake_resource_list = scheduler->resources;
do {
graph_rc = pcmk__execute_graph(transition);
} while (graph_rc == pcmk__graph_active);
fake_resource_list = NULL;
if (graph_rc != pcmk__graph_complete) {
out->err(out, "Transition failed: %s",
pcmk__graph_status2text(graph_rc));
pcmk__log_graph(LOG_ERR, transition);
out->err(out, "An invalid transition was produced");
}
pcmk__free_graph(transition);
if (!out->is_quiet(out)) {
// If not quiet, we'll need the resulting CIB for later display
xmlNode *cib_object = NULL;
int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
cib_sync_call|cib_scope_local);
CRM_ASSERT(rc == pcmk_ok);
pe_reset_working_set(scheduler);
scheduler->input = cib_object;
out->end_list(out);
}
return graph_rc;
}
int
pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out,
const pcmk_injections_t *injections, unsigned int flags,
uint32_t section_opts, const char *use_date,
const char *input_file, const char *graph_file,
const char *dot_file)
{
int printed = pcmk_rc_no_output;
int rc = pcmk_rc_ok;
xmlNodePtr input = NULL;
cib_t *cib = NULL;
rc = cib__signon_query(out, &cib, &input);
if (rc != pcmk_rc_ok) {
goto simulate_done;
}
reset(scheduler, input, out, use_date, flags);
cluster_status(scheduler);
if ((cib->variant == cib_native)
&& pcmk_is_set(section_opts, pcmk_section_times)) {
if (pcmk__our_nodename == NULL) {
// Currently used only in the times section
pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
}
scheduler->localhost = pcmk__our_nodename;
}
if (!out->is_quiet(out)) {
const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
printed = out->message(out, "maint-mode", scheduler->flags);
}
if (scheduler->disabled_resources || scheduler->blocked_resources) {
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
printed = out->info(out,
"%d of %d resource instances DISABLED and "
"%d BLOCKED from further action due to failure",
scheduler->disabled_resources,
scheduler->ninstances,
scheduler->blocked_resources);
}
/* Most formatted output headers use caps for each word, but this one
* only has the first word capitalized for compatibility with pcs.
*/
print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
section_opts, "Current cluster status",
(printed == pcmk_rc_ok));
printed = pcmk_rc_ok;
}
// If the user requested any injections, handle them
if ((injections->node_down != NULL)
|| (injections->node_fail != NULL)
|| (injections->node_up != NULL)
|| (injections->op_inject != NULL)
|| (injections->ticket_activate != NULL)
|| (injections->ticket_grant != NULL)
|| (injections->ticket_revoke != NULL)
|| (injections->ticket_standby != NULL)
|| (injections->watchdog != NULL)) {
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
pcmk__inject_scheduler_input(scheduler, cib, injections);
printed = pcmk_rc_ok;
rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
if (rc != pcmk_rc_ok) {
rc = pcmk_legacy2rc(rc);
goto simulate_done;
}
cleanup_calculations(scheduler);
reset(scheduler, input, out, use_date, flags);
cluster_status(scheduler);
}
if (input_file != NULL) {
rc = pcmk__xml_write_file(input, input_file, false, NULL);
if (rc != pcmk_rc_ok) {
goto simulate_done;
}
}
if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
pcmk__output_t *logger_out = NULL;
unsigned long long scheduler_flags = pcmk_sched_no_compat;
if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
scheduler_flags |= pcmk_sched_output_scores;
}
if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
scheduler_flags |= pcmk_sched_show_utilization;
}
if (pcmk_all_flags_set(scheduler->flags,
pcmk_sched_output_scores
|pcmk_sched_show_utilization)) {
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
out->begin_list(out, NULL, NULL,
"Assignment Scores and Utilization Information");
printed = pcmk_rc_ok;
} else if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
out->begin_list(out, NULL, NULL, "Assignment Scores");
printed = pcmk_rc_ok;
} else if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
out->begin_list(out, NULL, NULL, "Utilization Information");
printed = pcmk_rc_ok;
} else {
rc = pcmk__log_output_new(&logger_out);
if (rc != pcmk_rc_ok) {
goto simulate_done;
}
pe__register_messages(logger_out);
pcmk__register_lib_messages(logger_out);
scheduler->priv = logger_out;
}
pcmk__schedule_actions(input, scheduler_flags, scheduler);
if (logger_out == NULL) {
out->end_list(out);
} else {
logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
pcmk__output_free(logger_out);
scheduler->priv = out;
}
input = NULL; /* Don't try and free it twice */
if (graph_file != NULL) {
rc = pcmk__xml_write_file(scheduler->graph, graph_file, false,
NULL);
if (rc != pcmk_rc_ok) {
rc = pcmk_rc_graph_error;
goto simulate_done;
}
}
if (dot_file != NULL) {
rc = write_sim_dotfile(scheduler, dot_file,
pcmk_is_set(flags, pcmk_sim_all_actions),
pcmk_is_set(flags, pcmk_sim_verbose));
if (rc != pcmk_rc_ok) {
rc = pcmk_rc_dot_error;
goto simulate_done;
}
}
if (!out->is_quiet(out)) {
print_transition_summary(scheduler, printed == pcmk_rc_ok);
}
}
rc = pcmk_rc_ok;
if (!pcmk_is_set(flags, pcmk_sim_simulate)) {
goto simulate_done;
}
PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
!= pcmk__graph_complete) {
rc = pcmk_rc_invalid_transition;
}
if (out->is_quiet(out)) {
goto simulate_done;
}
set_effective_date(scheduler, true, use_date);
if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores);
}
if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization);
}
cluster_status(scheduler);
print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
true);
simulate_done:
cib__clean_up_connection(&cib);
return rc;
}
int
pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
const pcmk_injections_t *injections, unsigned int flags,
unsigned int section_opts, const char *use_date,
const char *input_file, const char *graph_file,
const char *dot_file)
{
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
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__simulate(scheduler, out, injections, flags, section_opts,
use_date, input_file, graph_file, dot_file);
pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
return rc;
}