diff --git a/cts/cli/regression.acls.exp b/cts/cli/regression.acls.exp
index 8df33a44c0..182232844d 100644
--- a/cts/cli/regression.acls.exp
+++ b/cts/cli/regression.acls.exp
@@ -1,4408 +1,4398 @@
 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: Configure some ACLs =#=#=#=
 =#=#=#= Current cib after: Configure some ACLs =#=#=#=
 <cib epoch="2" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Configure some ACLs - OK (0) =#=#=#=
 * Passed: cibadmin       - Configure some ACLs
 =#=#=#= Begin test: Enable ACLs =#=#=#=
 =#=#=#= Current cib after: Enable ACLs =#=#=#=
 <cib epoch="3" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Enable ACLs - OK (0) =#=#=#=
 * Passed: crm_attribute  - Enable ACLs
 =#=#=#= Begin test: Set cluster option =#=#=#=
 =#=#=#= Current cib after: Set cluster option =#=#=#=
 <cib epoch="4" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Set cluster option - OK (0) =#=#=#=
 * Passed: crm_attribute  - Set cluster option
 =#=#=#= Begin test: New ACL =#=#=#=
 =#=#=#= Current cib after: New ACL =#=#=#=
 <cib epoch="5" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: New ACL - OK (0) =#=#=#=
 * Passed: cibadmin       - New ACL
 =#=#=#= Begin test: Another ACL =#=#=#=
 =#=#=#= Current cib after: Another ACL =#=#=#=
 <cib epoch="6" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Another ACL - OK (0) =#=#=#=
 * Passed: cibadmin       - Another ACL
 =#=#=#= Begin test: Updated ACL =#=#=#=
 =#=#=#= Current cib after: Updated ACL =#=#=#=
 <cib epoch="7" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Updated ACL - OK (0) =#=#=#=
 * Passed: cibadmin       - Updated ACL
 =#=#=#= Begin test: unknownguy: Query configuration =#=#=#=
 Call failed: Permission denied
 =#=#=#= End test: unknownguy: Query configuration - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - unknownguy: Query configuration
 =#=#=#= Begin test: unknownguy: Set enable-acl =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: unknownguy: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - unknownguy: Set enable-acl
 =#=#=#= Begin test: unknownguy: Set stonith-enabled =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: unknownguy: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - unknownguy: Set stonith-enabled
 =#=#=#= Begin test: unknownguy: Create a resource =#=#=#=
-pcmk__check_acl 	trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id]
-pcmk__apply_creation_acl 	trace: Creation of <primitive> scaffolding with id="<unset>" is implicitly allowed
+pcmk__check_acl 	trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id='dummy']
+pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
-<failed>
-  <failed_update id="dummy" object-type="primitive" operation="cib_create" reason="Permission denied">
-    <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
-  </failed_update>
-</failed>
 =#=#=#= End test: unknownguy: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - unknownguy: Create a resource
 =#=#=#= Begin test: l33t-haxor: Query configuration =#=#=#=
 Call failed: Permission denied
 =#=#=#= End test: l33t-haxor: Query configuration - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - l33t-haxor: Query configuration
 =#=#=#= Begin test: l33t-haxor: Set enable-acl =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: l33t-haxor: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - l33t-haxor: Set enable-acl
 =#=#=#= Begin test: l33t-haxor: Set stonith-enabled =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: l33t-haxor: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - l33t-haxor: Set stonith-enabled
 =#=#=#= Begin test: l33t-haxor: Create a resource =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'l33t-haxor' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
 =#=#=#= End test: l33t-haxor: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - l33t-haxor: Create a resource
 =#=#=#= Begin test: niceguy: Query configuration =#=#=#=
 <cib epoch="7" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Query configuration - OK (0) =#=#=#=
 * Passed: cibadmin       - niceguy: Query configuration
 =#=#=#= Begin test: niceguy: Set enable-acl =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl'][@value]
 Error setting enable-acl=false (section=crm_config, set=<null>): Permission denied
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: niceguy: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - niceguy: Set enable-acl
 =#=#=#= Begin test: niceguy: Set stonith-enabled =#=#=#=
 pcmk__apply_creation_acl 	trace: ACLs allow creation of <nvpair> with id="cib-bootstrap-options-stonith-enabled"
 =#=#=#= Current cib after: niceguy: Set stonith-enabled =#=#=#=
 <cib epoch="8" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="false"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Set stonith-enabled - OK (0) =#=#=#=
 * Passed: crm_attribute  - niceguy: Set stonith-enabled
 =#=#=#= Begin test: niceguy: Create a resource =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Create a resource
 =#=#=#= Begin test: root: Query configuration =#=#=#=
 <cib epoch="8" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="false"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Query configuration - OK (0) =#=#=#=
 * Passed: cibadmin       - root: Query configuration
 =#=#=#= Begin test: root: Set stonith-enabled =#=#=#=
 =#=#=#= Current cib after: root: Set stonith-enabled =#=#=#=
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Set stonith-enabled - OK (0) =#=#=#=
 * Passed: crm_attribute  - root: Set stonith-enabled
 =#=#=#= Begin test: root: Create a resource =#=#=#=
 =#=#=#= Current cib after: root: Create a resource =#=#=#=
 <cib epoch="10" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Create a resource - OK (0) =#=#=#=
 * Passed: cibadmin       - root: Create a resource
 =#=#=#= Begin test: l33t-haxor: Create a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Create a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Create a resource meta attribute
 =#=#=#= Begin test: l33t-haxor: Query a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Query a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Query a resource meta attribute
 =#=#=#= Begin test: l33t-haxor: Remove a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Remove a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Remove a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 pcmk__apply_creation_acl 	trace: Creation of <meta_attributes> scaffolding with id="dummy-meta_attributes" is implicitly allowed
 pcmk__apply_creation_acl 	trace: ACLs allow creation of <nvpair> with id="dummy-meta_attributes-target-role"
 Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Stopped
 =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#=
 <cib epoch="11" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Stopped"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Create a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Create a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 Stopped
 =#=#=#= Current cib after: niceguy: Query a resource meta attribute =#=#=#=
 <cib epoch="11" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Stopped"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Query a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Query a resource meta attribute
 =#=#=#= Begin test: niceguy: 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-target-role name=target-role
 =#=#=#= Current cib after: niceguy: Remove a resource meta attribute =#=#=#=
 <cib epoch="12" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Remove a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Remove a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 pcmk__apply_creation_acl 	trace: ACLs allow creation of <nvpair> with id="dummy-meta_attributes-target-role"
 Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Started
 =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#=
 <cib epoch="13" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Create a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Create a resource meta attribute
 =#=#=#= Begin test: badidea: Query configuration - implied deny =#=#=#=
 <cib>
   <configuration>
     <resources>
       <primitive id="dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
   </configuration>
 </cib>
 =#=#=#= End test: badidea: Query configuration - implied deny - OK (0) =#=#=#=
 * Passed: cibadmin       - badidea: Query configuration - implied deny
 =#=#=#= Begin test: betteridea: Query configuration - explicit deny =#=#=#=
 <cib>
   <configuration>
     <resources>
       <primitive id="dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
   </configuration>
 </cib>
 =#=#=#= End test: betteridea: Query configuration - explicit deny - OK (0) =#=#=#=
 * Passed: cibadmin       - betteridea: Query configuration - explicit deny
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - remove acls =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/acls
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - remove acls - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - remove acls
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - create resource =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy2']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy2"
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - create resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - create resource
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="false"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - modify attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl'][@value]
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - modify attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - modify attribute (deny)
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - delete attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl']
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - delete attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - delete attribute (deny)
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - create attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - create attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - create attribute (deny)
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - create attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - create attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - create attribute (direct allow)
 <cib epoch="15" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - modify attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - modify attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - modify attribute (direct allow)
 <cib epoch="16" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - delete attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - delete attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - delete attribute (direct allow)
 <cib epoch="17" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - create attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - create attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - create attribute (inherited allow)
 <cib epoch="18" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - modify attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - modify attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - modify attribute (inherited allow)
 <cib epoch="19" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - delete attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - delete attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - delete attribute (inherited allow)
 <cib epoch="20" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - create attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - create attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - create attribute (allow overrides deny)
 <cib epoch="21" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - modify attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - modify attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - modify attribute (allow overrides deny)
 <cib epoch="22" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - delete attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - delete attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - delete attribute (allow overrides deny)
 <cib epoch="23" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - create attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - create attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - create attribute (deny overrides allow)
 <cib epoch="24" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - modify attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - modify attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - modify attribute (deny overrides allow)
 <cib epoch="25" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_user id="l33t-haxor">
         <deny id="crook-nothing" xpath="/cib"/>
       </acl_user>
       <acl_user id="niceguy">
         <role_ref id="observer"/>
       </acl_user>
       <acl_user id="bob">
         <role_ref id="admin"/>
       </acl_user>
       <acl_user id="joe">
         <role_ref id="super_user"/>
       </acl_user>
       <acl_user id="mike">
         <role_ref id="rsc_writer"/>
       </acl_user>
       <acl_user id="chris">
         <role_ref id="rsc_denied"/>
       </acl_user>
       <acl_role id="observer">
         <read id="observer-read-1" xpath="/cib"/>
         <write id="observer-write-1" xpath="//nvpair[@name='stonith-enabled']"/>
         <write id="observer-write-2" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <read id="admin-read-1" xpath="/cib"/>
         <write id="admin-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <write id="super_user-write-1" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <deny id="rsc-writer-deny-1" xpath="/cib"/>
         <write id="rsc-writer-write-1" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <write id="rsc-denied-write-1" xpath="/cib"/>
         <deny id="rsc-denied-deny-1" xpath="//resources"/>
       </acl_role>
       <acl_user id="badidea">
         <read id="badidea-resources" xpath="//meta_attributes"/>
       </acl_user>
       <acl_user id="betteridea">
         <deny id="betteridea-nothing" xpath="/cib"/>
         <read id="betteridea-resources" xpath="//meta_attributes"/>
       </acl_user>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - delete attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - delete attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - delete attribute (deny overrides allow)
 
 
     !#!#!#!#! Upgrading to latest CIB schema and re-testing !#!#!#!#!
 =#=#=#= Begin test: root: Upgrade to latest CIB schema =#=#=#=
 =#=#=#= Current cib after: root: Upgrade to latest CIB schema =#=#=#=
 <cib epoch="2" num_updates="0" admin_epoch="1">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Upgrade to latest CIB schema - OK (0) =#=#=#=
 * Passed: cibadmin       - root: Upgrade to latest CIB schema
 =#=#=#= Begin test: unknownguy: Query configuration =#=#=#=
 Call failed: Permission denied
 =#=#=#= End test: unknownguy: Query configuration - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - unknownguy: Query configuration
 =#=#=#= Begin test: unknownguy: Set enable-acl =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: unknownguy: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - unknownguy: Set enable-acl
 =#=#=#= Begin test: unknownguy: Set stonith-enabled =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: unknownguy: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - unknownguy: Set stonith-enabled
 =#=#=#= Begin test: unknownguy: Create a resource =#=#=#=
-pcmk__check_acl 	trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id]
-pcmk__apply_creation_acl 	trace: Creation of <primitive> scaffolding with id="<unset>" is implicitly allowed
+pcmk__check_acl 	trace: User 'unknownguy' without ACLs denied read/write access to /cib/configuration/resources/primitive[@id='dummy']
+pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
-<failed>
-  <failed_update id="dummy" object-type="primitive" operation="cib_create" reason="Permission denied">
-    <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
-  </failed_update>
-</failed>
 =#=#=#= End test: unknownguy: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - unknownguy: Create a resource
 =#=#=#= Begin test: l33t-haxor: Query configuration =#=#=#=
 Call failed: Permission denied
 =#=#=#= End test: l33t-haxor: Query configuration - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - l33t-haxor: Query configuration
 =#=#=#= Begin test: l33t-haxor: Set enable-acl =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: l33t-haxor: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - l33t-haxor: Set enable-acl
 =#=#=#= Begin test: l33t-haxor: Set stonith-enabled =#=#=#=
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: l33t-haxor: Set stonith-enabled - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - l33t-haxor: Set stonith-enabled
 =#=#=#= Begin test: l33t-haxor: Create a resource =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'l33t-haxor' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
 =#=#=#= End test: l33t-haxor: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - l33t-haxor: Create a resource
 =#=#=#= Begin test: niceguy: Query configuration =#=#=#=
 <cib epoch="2" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Query configuration - OK (0) =#=#=#=
 * Passed: cibadmin       - niceguy: Query configuration
 =#=#=#= Begin test: niceguy: Set enable-acl =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl'][@value]
 Error setting enable-acl=false (section=crm_config, set=<null>): Permission denied
 crm_attribute: Error performing operation: Permission denied
 =#=#=#= End test: niceguy: Set enable-acl - Insufficient privileges (4) =#=#=#=
 * Passed: crm_attribute  - niceguy: Set enable-acl
 =#=#=#= Begin test: niceguy: Set stonith-enabled =#=#=#=
 =#=#=#= Current cib after: niceguy: Set stonith-enabled =#=#=#=
 <cib epoch="3" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="false"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Set stonith-enabled - OK (0) =#=#=#=
 * Passed: crm_attribute  - niceguy: Set stonith-enabled
 =#=#=#= Begin test: niceguy: Create a resource =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy"
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Create a resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Create a resource
 =#=#=#= Begin test: root: Query configuration =#=#=#=
 <cib epoch="3" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="false"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Query configuration - OK (0) =#=#=#=
 * Passed: cibadmin       - root: Query configuration
 =#=#=#= Begin test: root: Set stonith-enabled =#=#=#=
 =#=#=#= Current cib after: root: Set stonith-enabled =#=#=#=
 <cib epoch="4" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources/>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Set stonith-enabled - OK (0) =#=#=#=
 * Passed: crm_attribute  - root: Set stonith-enabled
 =#=#=#= Begin test: root: Create a resource =#=#=#=
 =#=#=#= Current cib after: root: Create a resource =#=#=#=
 <cib epoch="5" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: root: Create a resource - OK (0) =#=#=#=
 * Passed: cibadmin       - root: Create a resource
 =#=#=#= Begin test: l33t-haxor: Create a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Create a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Create a resource meta attribute
 =#=#=#= Begin test: l33t-haxor: Query a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Query a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Query a resource meta attribute
 =#=#=#= Begin test: l33t-haxor: Remove a resource meta attribute =#=#=#=
 crm_resource: Error performing operation: Insufficient privileges
 =#=#=#= End test: l33t-haxor: Remove a resource meta attribute - Insufficient privileges (4) =#=#=#=
 * Passed: crm_resource   - l33t-haxor: Remove a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 pcmk__apply_creation_acl 	trace: Creation of <meta_attributes> scaffolding with id="dummy-meta_attributes" is implicitly allowed
 pcmk__apply_creation_acl 	trace: ACLs allow creation of <nvpair> with id="dummy-meta_attributes-target-role"
 Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Stopped
 =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#=
 <cib epoch="6" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Stopped"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Create a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Create a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 Stopped
 =#=#=#= Current cib after: niceguy: Query a resource meta attribute =#=#=#=
 <cib epoch="6" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Stopped"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Query a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Query a resource meta attribute
 =#=#=#= Begin test: niceguy: 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-target-role name=target-role
 =#=#=#= Current cib after: niceguy: Remove a resource meta attribute =#=#=#=
 <cib epoch="7" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes"/>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Remove a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Remove a resource meta attribute
 =#=#=#= Begin test: niceguy: 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
 pcmk__apply_creation_acl 	trace: ACLs allow creation of <nvpair> with id="dummy-meta_attributes-target-role"
 Set 'dummy' option: id=dummy-meta_attributes-target-role set=dummy-meta_attributes name=target-role value=Started
 =#=#=#= Current cib after: niceguy: Create a resource meta attribute =#=#=#=
 <cib epoch="8" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: niceguy: Create a resource meta attribute - OK (0) =#=#=#=
 * Passed: crm_resource   - niceguy: Create a resource meta attribute
 =#=#=#= Begin test: badidea: Query configuration - implied deny =#=#=#=
 <cib>
   <configuration>
     <resources>
       <primitive id="dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
   </configuration>
 </cib>
 =#=#=#= End test: badidea: Query configuration - implied deny - OK (0) =#=#=#=
 * Passed: cibadmin       - badidea: Query configuration - implied deny
 =#=#=#= Begin test: betteridea: Query configuration - explicit deny =#=#=#=
 <cib>
   <configuration>
     <resources>
       <primitive id="dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
   </configuration>
 </cib>
 =#=#=#= End test: betteridea: Query configuration - explicit deny - OK (0) =#=#=#=
 * Passed: cibadmin       - betteridea: Query configuration - explicit deny
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - remove acls =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/acls
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - remove acls - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - remove acls
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - create resource =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy2']
 pcmk__apply_creation_acl 	trace: ACLs disallow creation of <primitive> with id="dummy2"
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - create resource - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - create resource
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="false"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - modify attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl'][@value]
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - modify attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - modify attribute (deny)
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - delete attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/crm_config/cluster_property_set[@id='cib-bootstrap-options']/nvpair[@id='cib-bootstrap-options-enable-acl']
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - delete attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - delete attribute (deny)
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: niceguy: Replace - create attribute (deny) =#=#=#=
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib[@epoch]
 pcmk__check_acl 	trace: Default ACL denies user 'niceguy' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: niceguy: Replace - create attribute (deny) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - niceguy: Replace - create attribute (deny)
 <cib epoch="9" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - create attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - create attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - create attribute (direct allow)
 <cib epoch="10" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting">
         <meta_attributes id="dummy-meta_attributes">
           <nvpair id="dummy-meta_attributes-target-role" name="target-role" value="Started"/>
         </meta_attributes>
       </primitive>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - modify attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - modify attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - modify attribute (direct allow)
 <cib epoch="11" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: bob: Replace - delete attribute (direct allow) =#=#=#=
 =#=#=#= End test: bob: Replace - delete attribute (direct allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - bob: Replace - delete attribute (direct allow)
 <cib epoch="12" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - create attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - create attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - create attribute (inherited allow)
 <cib epoch="13" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - modify attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - modify attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - modify attribute (inherited allow)
 <cib epoch="14" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: joe: Replace - delete attribute (inherited allow) =#=#=#=
 =#=#=#= End test: joe: Replace - delete attribute (inherited allow) - OK (0) =#=#=#=
 * Passed: cibadmin       - joe: Replace - delete attribute (inherited allow)
 <cib epoch="15" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - create attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - create attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - create attribute (allow overrides deny)
 <cib epoch="16" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - modify attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - modify attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - modify attribute (allow overrides deny)
 <cib epoch="17" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: mike: Replace - delete attribute (allow overrides deny) =#=#=#=
 =#=#=#= End test: mike: Replace - delete attribute (allow overrides deny) - OK (0) =#=#=#=
 * Passed: cibadmin       - mike: Replace - delete attribute (allow overrides deny)
 <cib epoch="18" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="nothing interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - create attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - create attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - create attribute (deny overrides allow)
 <cib epoch="19" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy" description="something interesting"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - modify attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy'][@description]
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - modify attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - modify attribute (deny overrides allow)
 <cib epoch="20" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config>
       <cluster_property_set id="cib-bootstrap-options">
         <nvpair id="cib-bootstrap-options-enable-acl" name="enable-acl" value="true"/>
         <nvpair id="cib-bootstrap-options-no-quorum-policy" name="no-quorum-policy" value="ignore"/>
         <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" value="true"/>
       </cluster_property_set>
     </crm_config>
     <nodes/>
     <resources>
       <primitive id="dummy" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints/>
     <acls>
       <acl_target id="l33t-haxor">
         <role id="auto-l33t-haxor"/>
       </acl_target>
       <acl_role id="auto-l33t-haxor">
         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
       </acl_role>
       <acl_target id="niceguy">
         <role id="observer"/>
       </acl_target>
       <acl_target id="bob">
         <role id="admin"/>
       </acl_target>
       <acl_target id="joe">
         <role id="super_user"/>
       </acl_target>
       <acl_target id="mike">
         <role id="rsc_writer"/>
       </acl_target>
       <acl_target id="chris">
         <role id="rsc_denied"/>
       </acl_target>
       <acl_role id="observer">
         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
       </acl_role>
       <acl_role id="admin">
         <acl_permission id="admin-read-1" kind="read" xpath="/cib"/>
         <acl_permission id="admin-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="super_user">
         <acl_permission id="super_user-write-1" kind="write" xpath="/cib"/>
       </acl_role>
       <acl_role id="rsc_writer">
         <acl_permission id="rsc-writer-deny-1" kind="deny" xpath="/cib"/>
         <acl_permission id="rsc-writer-write-1" kind="write" xpath="//resources"/>
       </acl_role>
       <acl_role id="rsc_denied">
         <acl_permission id="rsc-denied-write-1" kind="write" xpath="/cib"/>
         <acl_permission id="rsc-denied-deny-1" kind="deny" xpath="//resources"/>
       </acl_role>
       <acl_target id="badidea">
         <role id="auto-badidea"/>
       </acl_target>
       <acl_role id="auto-badidea">
         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
       <acl_target id="betteridea">
         <role id="auto-betteridea"/>
       </acl_target>
       <acl_role id="auto-betteridea">
         <acl_permission id="betteridea-nothing" kind="deny" xpath="/cib"/>
         <acl_permission id="betteridea-resources" kind="read" xpath="//meta_attributes"/>
       </acl_role>
     </acls>
   </configuration>
   <status/>
 </cib>
 =#=#=#= Begin test: chris: Replace - delete attribute (deny overrides allow) =#=#=#=
 pcmk__check_acl 	trace: Parent ACL denies user 'chris' read/write access to /cib/configuration/resources/primitive[@id='dummy']
 Call failed: Permission denied
 =#=#=#= End test: chris: Replace - delete attribute (deny overrides allow) - Insufficient privileges (4) =#=#=#=
 * Passed: cibadmin       - chris: Replace - delete attribute (deny overrides allow)
diff --git a/cts/cli/regression.validity.exp b/cts/cli/regression.validity.exp
index 188f93e327..16d052cb00 100644
--- a/cts/cli/regression.validity.exp
+++ b/cts/cli/regression.validity.exp
@@ -1,566 +1,566 @@
 Created new pacemaker configuration
 A new shadow instance was created. To begin using it, enter the following into your shell:
 	export CIB_shadow=cts-cli
 =#=#=#= Begin test: Try to make resulting CIB invalid (enum violation) =#=#=#=
    1 <cib epoch="5" num_updates="0" admin_epoch="0">
    2   <configuration>
    3     <crm_config/>
    4     <nodes/>
    5     <resources>
    6       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
    7       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
    8     </resources>
    9     <constraints>
   10       <rsc_order id="ord_1-2" first="dummy1" first-action="break" then="dummy2"/>
   11     </constraints>
   12   </configuration>
   13   <status/>
   14 </cib>
   15 
 Call failed: Update does not conform to the configured schema
 =#=#=#= Current cib after: Try to make resulting CIB invalid (enum violation) =#=#=#=
 <cib epoch="4" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources>
       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints>
       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
     </constraints>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Try to make resulting CIB invalid (enum violation) - Invalid configuration (78) =#=#=#=
 * Passed: cibadmin       - Try to make resulting CIB invalid (enum violation)
 =#=#=#= Begin test: Run crm_simulate with invalid CIB (enum violation) =#=#=#=
 update_validation 	debug: Testing 'pacemaker-1.2' validation (1 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-1.2 validation failed
 update_validation 	debug: Testing 'pacemaker-1.3' validation (2 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-1.3 validation failed
 update_validation 	debug: Testing 'pacemaker-2.0' validation (3 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.0 validation failed
 update_validation 	debug: Testing 'pacemaker-2.1' validation (4 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.1 validation failed
 update_validation 	debug: Testing 'pacemaker-2.2' validation (5 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.2 validation failed
 update_validation 	debug: Testing 'pacemaker-2.3' validation (6 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.3 validation failed
 update_validation 	debug: Testing 'pacemaker-2.4' validation (7 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.4 validation failed
 update_validation 	debug: Testing 'pacemaker-2.5' validation (8 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.5 validation failed
 update_validation 	debug: Testing 'pacemaker-2.6' validation (9 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.6 validation failed
 update_validation 	debug: Testing 'pacemaker-2.7' validation (10 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.7 validation failed
 update_validation 	debug: Testing 'pacemaker-2.8' validation (11 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.8 validation failed
 update_validation 	debug: Testing 'pacemaker-2.9' validation (12 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.9 validation failed
 update_validation 	debug: Testing 'pacemaker-2.10' validation (13 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-2.10 validation failed
 update_validation 	debug: Testing 'pacemaker-3.0' validation (14 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.0 validation failed
 update_validation 	debug: Testing 'pacemaker-3.1' validation (15 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.1 validation failed
 update_validation 	debug: Testing 'pacemaker-3.2' validation (16 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.2 validation failed
 update_validation 	debug: Testing 'pacemaker-3.3' validation (17 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.3 validation failed
 update_validation 	debug: Testing 'pacemaker-3.4' validation (18 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.4 validation failed
 update_validation 	debug: Testing 'pacemaker-3.5' validation (19 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.5 validation failed
 update_validation 	debug: Testing 'pacemaker-3.6' validation (20 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.6 validation failed
 update_validation 	debug: Testing 'pacemaker-3.7' validation (21 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.7 validation failed
 update_validation 	debug: Testing 'pacemaker-3.8' validation (22 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.8 validation failed
 update_validation 	debug: Testing 'pacemaker-3.9' validation (23 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.9 validation failed
 update_validation 	debug: Testing 'pacemaker-3.10' validation (24 of X)
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 update_validation 	trace: pacemaker-3.10 validation failed
 Cannot upgrade configuration (claiming schema pacemaker-1.2) to at least pacemaker-3.0 because it does not validate with any schema from pacemaker-1.2 to pacemaker-3.10
 =#=#=#= End test: Run crm_simulate with invalid CIB (enum violation) - Invalid configuration (78) =#=#=#=
 * Passed: crm_simulate   - Run crm_simulate with invalid CIB (enum violation)
 =#=#=#= Begin test: Try to make resulting CIB invalid (unrecognized validate-with) =#=#=#=
    1 <cib epoch="4" num_updates="1" admin_epoch="0">
    2   <configuration>
    3     <crm_config/>
    4     <nodes/>
    5     <resources>
    6       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
    7       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
    8     </resources>
    9     <constraints>
   10       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
   11     </constraints>
   12   </configuration>
   13   <status/>
   14 </cib>
   15 
 Call failed: Update does not conform to the configured schema
 =#=#=#= Current cib after: Try to make resulting CIB invalid (unrecognized validate-with) =#=#=#=
 <cib epoch="4" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources>
       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints>
       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
     </constraints>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Try to make resulting CIB invalid (unrecognized validate-with) - Invalid configuration (78) =#=#=#=
 * Passed: cibadmin       - Try to make resulting CIB invalid (unrecognized validate-with)
 =#=#=#= Begin test: Run crm_simulate with invalid CIB (unrecognized validate-with) =#=#=#=
 update_validation 	debug: Unknown validation schema
 update_validation 	debug: Testing 'pacemaker-1.0' validation (0 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-1.0 validation failed
 update_validation 	debug: Testing 'pacemaker-1.2' validation (1 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-1.2 validation failed
 update_validation 	debug: Testing 'pacemaker-1.3' validation (2 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-1.3 validation failed
 update_validation 	debug: Testing 'pacemaker-2.0' validation (3 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.0 validation failed
 update_validation 	debug: Testing 'pacemaker-2.1' validation (4 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.1 validation failed
 update_validation 	debug: Testing 'pacemaker-2.2' validation (5 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.2 validation failed
 update_validation 	debug: Testing 'pacemaker-2.3' validation (6 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.3 validation failed
 update_validation 	debug: Testing 'pacemaker-2.4' validation (7 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.4 validation failed
 update_validation 	debug: Testing 'pacemaker-2.5' validation (8 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.5 validation failed
 update_validation 	debug: Testing 'pacemaker-2.6' validation (9 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.6 validation failed
 update_validation 	debug: Testing 'pacemaker-2.7' validation (10 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.7 validation failed
 update_validation 	debug: Testing 'pacemaker-2.8' validation (11 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.8 validation failed
 update_validation 	debug: Testing 'pacemaker-2.9' validation (12 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.9 validation failed
 update_validation 	debug: Testing 'pacemaker-2.10' validation (13 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-2.10 validation failed
 update_validation 	debug: Testing 'pacemaker-3.0' validation (14 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.0 validation failed
 update_validation 	debug: Testing 'pacemaker-3.1' validation (15 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.1 validation failed
 update_validation 	debug: Testing 'pacemaker-3.2' validation (16 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.2 validation failed
 update_validation 	debug: Testing 'pacemaker-3.3' validation (17 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.3 validation failed
 update_validation 	debug: Testing 'pacemaker-3.4' validation (18 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.4 validation failed
 update_validation 	debug: Testing 'pacemaker-3.5' validation (19 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.5 validation failed
 update_validation 	debug: Testing 'pacemaker-3.6' validation (20 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.6 validation failed
 update_validation 	debug: Testing 'pacemaker-3.7' validation (21 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.7 validation failed
 update_validation 	debug: Testing 'pacemaker-3.8' validation (22 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.8 validation failed
 update_validation 	debug: Testing 'pacemaker-3.9' validation (23 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.9 validation failed
 update_validation 	debug: Testing 'pacemaker-3.10' validation (24 of X)
 element cib: Relax-NG validity error : Invalid attribute validate-with for element cib
 update_validation 	trace: pacemaker-3.10 validation failed
 Cannot upgrade configuration (claiming schema pacemaker-9999.0) to at least pacemaker-3.0 because it does not validate with any schema from unknown to pacemaker-3.10
 =#=#=#= End test: Run crm_simulate with invalid CIB (unrecognized validate-with) - Invalid configuration (78) =#=#=#=
 * Passed: crm_simulate   - Run crm_simulate with invalid CIB (unrecognized validate-with)
 =#=#=#= Begin test: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) =#=#=#=
-   1 <cib epoch="4" num_updates="0" admin_epoch="0">
+   1 <cib epoch="5" num_updates="0" admin_epoch="0">
    2   <configuration>
    3     <crm_config/>
    4     <nodes/>
    5     <resources>
    6       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
    7       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
    8     </resources>
    9     <constraints>
   10       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
   11     </constraints>
   12     <tags/>
   13   </configuration>
   14   <status/>
   15 </cib>
   16 
 Call failed: Update does not conform to the configured schema
 =#=#=#= Current cib after: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) =#=#=#=
 <cib epoch="4" num_updates="0" admin_epoch="0">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources>
       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints>
       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
     </constraints>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) - Invalid configuration (78) =#=#=#=
 * Passed: cibadmin       - Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1)
 =#=#=#= Begin test: Run crm_simulate with invalid, but possibly recoverable CIB (valid with X.Y+1) =#=#=#=
 update_validation 	debug: Testing 'pacemaker-1.2' validation (1 of X)
 element tags: Relax-NG validity error : Element configuration has extra content: tags
 update_validation 	trace: pacemaker-1.2 validation failed
 update_validation 	debug: Testing 'pacemaker-1.3' validation (2 of X)
 update_validation 	debug: pacemaker-1.3-style configuration is also valid for pacemaker-2.0
 update_validation 	debug: Testing 'pacemaker-2.0' validation (3 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.0
 update_validation 	debug: pacemaker-2.0-style configuration is also valid for pacemaker-2.1
 update_validation 	debug: Testing 'pacemaker-2.1' validation (4 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.1
 update_validation 	debug: pacemaker-2.1-style configuration is also valid for pacemaker-2.2
 update_validation 	debug: Testing 'pacemaker-2.2' validation (5 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.2
 update_validation 	debug: pacemaker-2.2-style configuration is also valid for pacemaker-2.3
 update_validation 	debug: Testing 'pacemaker-2.3' validation (6 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.3
 update_validation 	debug: pacemaker-2.3-style configuration is also valid for pacemaker-2.4
 update_validation 	debug: Testing 'pacemaker-2.4' validation (7 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.4
 update_validation 	debug: pacemaker-2.4-style configuration is also valid for pacemaker-2.5
 update_validation 	debug: Testing 'pacemaker-2.5' validation (8 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.5
 update_validation 	debug: pacemaker-2.5-style configuration is also valid for pacemaker-2.6
 update_validation 	debug: Testing 'pacemaker-2.6' validation (9 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.6
 update_validation 	debug: pacemaker-2.6-style configuration is also valid for pacemaker-2.7
 update_validation 	debug: Testing 'pacemaker-2.7' validation (10 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.7
 update_validation 	debug: pacemaker-2.7-style configuration is also valid for pacemaker-2.8
 update_validation 	debug: Testing 'pacemaker-2.8' validation (11 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.8
 update_validation 	debug: pacemaker-2.8-style configuration is also valid for pacemaker-2.9
 update_validation 	debug: Testing 'pacemaker-2.9' validation (12 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.9
 update_validation 	debug: pacemaker-2.9-style configuration is also valid for pacemaker-2.10
 update_validation 	debug: Testing 'pacemaker-2.10' validation (13 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-2.10
 update_validation 	debug: pacemaker-2.10-style configuration is also valid for pacemaker-3.0
 update_validation 	debug: Testing 'pacemaker-3.0' validation (14 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.0
 update_validation 	debug: pacemaker-3.0-style configuration is also valid for pacemaker-3.1
 update_validation 	debug: Testing 'pacemaker-3.1' validation (15 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.1
 update_validation 	debug: pacemaker-3.1-style configuration is also valid for pacemaker-3.2
 update_validation 	debug: Testing 'pacemaker-3.2' validation (16 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.2
 update_validation 	debug: pacemaker-3.2-style configuration is also valid for pacemaker-3.3
 update_validation 	debug: Testing 'pacemaker-3.3' validation (17 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.3
 update_validation 	debug: pacemaker-3.3-style configuration is also valid for pacemaker-3.4
 update_validation 	debug: Testing 'pacemaker-3.4' validation (18 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.4
 update_validation 	debug: pacemaker-3.4-style configuration is also valid for pacemaker-3.5
 update_validation 	debug: Testing 'pacemaker-3.5' validation (19 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.5
 update_validation 	debug: pacemaker-3.5-style configuration is also valid for pacemaker-3.6
 update_validation 	debug: Testing 'pacemaker-3.6' validation (20 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.6
 update_validation 	debug: pacemaker-3.6-style configuration is also valid for pacemaker-3.7
 update_validation 	debug: Testing 'pacemaker-3.7' validation (21 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.7
 update_validation 	debug: pacemaker-3.7-style configuration is also valid for pacemaker-3.8
 update_validation 	debug: Testing 'pacemaker-3.8' validation (22 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.8
 update_validation 	debug: pacemaker-3.8-style configuration is also valid for pacemaker-3.9
 update_validation 	debug: Testing 'pacemaker-3.9' validation (23 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.9
 update_validation 	debug: pacemaker-3.9-style configuration is also valid for pacemaker-3.10
 update_validation 	debug: Testing 'pacemaker-3.10' validation (24 of X)
 update_validation 	debug: Configuration valid for schema: pacemaker-3.10
 update_validation 	trace: Stopping at pacemaker-3.10
 update_validation 	info: Transformed the configuration from pacemaker-1.2 to pacemaker-3.10
 unpack_resources 	error: Resource start-up disabled since no STONITH resources have been defined
 unpack_resources 	error: Either configure some or disable STONITH with the stonith-enabled option
 unpack_resources 	error: NOTE: Clusters with shared data need STONITH to ensure data integrity
 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:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 
 Transition Summary:
 
 Executing Cluster Transition:
 
 Revised Cluster Status:
   * Full List of Resources:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 =#=#=#= End test: Run crm_simulate with invalid, but possibly recoverable CIB (valid with X.Y+1) - OK (0) =#=#=#=
 * Passed: crm_simulate   - Run crm_simulate with invalid, but possibly recoverable CIB (valid with X.Y+1)
 =#=#=#= Begin test: Make resulting CIB valid, although without validate-with attribute =#=#=#=
 =#=#=#= Current cib after: Make resulting CIB valid, although without validate-with attribute =#=#=#=
 <cib epoch="4" num_updates="1" admin_epoch="0" validate-with="none">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources>
       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints>
       <rsc_order id="ord_1-2" first="dummy1" first-action="start" then="dummy2"/>
     </constraints>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Make resulting CIB valid, although without validate-with attribute - OK (0) =#=#=#=
 * Passed: cibadmin       - Make resulting CIB valid, although without validate-with attribute
 =#=#=#= Begin test: Run crm_simulate with valid CIB, but without validate-with attribute =#=#=#=
 Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
 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:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 
 Transition Summary:
 
 Executing Cluster Transition:
 
 Revised Cluster Status:
   * Full List of Resources:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 =#=#=#= End test: Run crm_simulate with valid CIB, but without validate-with attribute - OK (0) =#=#=#=
 * Passed: crm_simulate   - Run crm_simulate with valid CIB, but without validate-with attribute
 =#=#=#= Begin test: Make resulting CIB invalid, and without validate-with attribute =#=#=#=
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 =#=#=#= Current cib after: Make resulting CIB invalid, and without validate-with attribute =#=#=#=
 <cib epoch="41" num_updates="0" admin_epoch="0" validate-with="none">
   <configuration>
     <crm_config/>
     <nodes/>
     <resources>
       <primitive id="dummy1" class="ocf" provider="pacemaker" type="Dummy"/>
       <primitive id="dummy2" class="ocf" provider="pacemaker" type="Dummy"/>
     </resources>
     <constraints>
       <rsc_order id="ord_1-2" first="dummy1" first-action="break" then="dummy2"/>
     </constraints>
   </configuration>
   <status/>
 </cib>
 =#=#=#= End test: Make resulting CIB invalid, and without validate-with attribute - OK (0) =#=#=#=
 * Passed: cibadmin       - Make resulting CIB invalid, and without validate-with attribute
 =#=#=#= Begin test: Run crm_simulate with invalid CIB, also without validate-with attribute =#=#=#=
 Schema validation of configuration is disabled (enabling is encouraged and prevents common misconfigurations)
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order
 validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order
 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:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 
 Transition Summary:
 
 Executing Cluster Transition:
 
 Revised Cluster Status:
   * Full List of Resources:
     * dummy1	(ocf:pacemaker:Dummy):	 Stopped
     * dummy2	(ocf:pacemaker:Dummy):	 Stopped
 =#=#=#= End test: Run crm_simulate with invalid CIB, also without validate-with attribute - OK (0) =#=#=#=
 * Passed: crm_simulate   - Run crm_simulate with invalid CIB, also without validate-with attribute
diff --git a/include/crm/cib/cib_types.h b/include/crm/cib/cib_types.h
index dad3c8f45d..8662dac56a 100644
--- a/include/crm/cib/cib_types.h
+++ b/include/crm/cib/cib_types.h
@@ -1,359 +1,364 @@
 /*
  * Copyright 2004-2023 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef PCMK__CRM_CIB_CIB_TYPES__H
 #  define PCMK__CRM_CIB_CIB_TYPES__H
 
 #  include <glib.h>             // gboolean, GList
 #  include <libxml/tree.h>      // xmlNode
 #  include <crm/common/ipc.h>
 #  include <crm/common/xml.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
  * \file
  * \brief Data types for Cluster Information Base access
  * \ingroup cib
  */
 
 enum cib_variant {
     cib_undefined = 0,
     cib_native    = 1,
     cib_file      = 2,
     cib_remote    = 3,
 
 #if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
     //! \deprecated This value will be removed in a future release
     cib_database  = 4,
 #endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
 };
 
 enum cib_state {
     cib_connected_command,
     cib_connected_query,
     cib_disconnected
 };
 
 enum cib_conn_type {
     cib_command,
     cib_query,
     cib_no_connection,
     cib_command_nonblocking,
 };
 
 enum cib_call_options {
     cib_none            = 0,
     cib_verbose         = (1 << 0),  //!< Prefer stderr to logs
     cib_xpath           = (1 << 1),
     cib_multiple        = (1 << 2),
     cib_can_create      = (1 << 3),
     cib_discard_reply   = (1 << 4),
     cib_no_children     = (1 << 5),
     cib_xpath_address   = (1 << 6),
 
     //! \deprecated This value will be removed in a future release
     cib_mixed_update    = (1 << 7),
 
     /* @COMPAT: cib_scope_local is processed only in the legacy function
      * parse_local_options_v1().
      *
      * If (host == NULL):
      * * In legacy mode, the CIB manager forwards a request to the primary
      *   instance unless cib_scope_local is set or the local node is primary.
      * * Outside of legacy mode:
      *   * If a request modifies the CIB, the CIB manager forwards it to all
      *     nodes.
      *   * Otherwise, the CIB manager processes the request locally.
      *
      * There is no current use case for this implementing this flag in
      * non-legacy mode.
      */
 
     //! \deprecated This value will be removed in a future release
     cib_scope_local     = (1 << 8),
 
     cib_dryrun          = (1 << 9),
 
     /*!
      * \brief Process request when the client commits the active transaction
      *
      * Add the request to the client's active transaction instead of processing
      * it immediately. If the client has no active transaction, or if the
      * request is not supported in transactions, the call will fail.
      *
      * The request is added to the transaction synchronously, and the return
      * value indicates whether it was added successfully.
      *
      * Refer to \p cib_api_operations_t:init_transaction() and
      * \p cib_api_operations_t:end_transaction() for more details on CIB
      * transactions.
      */
     cib_transaction     = (1 << 10),
 
     cib_sync_call       = (1 << 12),
     cib_no_mtime        = (1 << 13),
 
 #if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
     //! \deprecated This value will be removed in a future release
     cib_zero_copy       = (1 << 14),
 #endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
 
     cib_inhibit_notify  = (1 << 16),
 
 #if !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
     //! \deprecated This value will be removed in a future release
     cib_quorum_override = (1 << 20),
 #endif // !defined(PCMK_ALLOW_DEPRECATED) || (PCMK_ALLOW_DEPRECATED == 1)
 
     //! \deprecated This value will be removed in a future release
     cib_inhibit_bcast   = (1 << 24),
 
     cib_force_diff      = (1 << 28),
 };
 
 typedef struct cib_s cib_t;
 
 typedef struct cib_api_operations_s {
     int (*signon) (cib_t *cib, const char *name, enum cib_conn_type type);
 
     //! \deprecated This method will be removed and should not be used
     int (*signon_raw) (cib_t *cib, const char *name, enum cib_conn_type type,
                        int *event_fd);
 
     int (*signoff) (cib_t *cib);
     int (*free) (cib_t *cib);
 
     //! \deprecated This method will be removed and should not be used
     int (*set_op_callback) (cib_t *cib, void (*callback) (const xmlNode *msg,
                                                           int callid, int rc,
                                                           xmlNode *output));
 
     int (*add_notify_callback) (cib_t *cib, const char *event,
                                 void (*callback) (const char *event,
                                                   xmlNode *msg));
     int (*del_notify_callback) (cib_t *cib, const char *event,
                                 void (*callback) (const char *event,
                                                   xmlNode *msg));
     int (*set_connection_dnotify) (cib_t *cib,
                                    void (*dnotify) (gpointer user_data));
 
     //! \deprecated This method will be removed and should not be used
     int (*inputfd) (cib_t *cib);
 
     //! \deprecated This method will be removed and should not be used
     int (*noop) (cib_t *cib, int call_options);
 
     int (*ping) (cib_t *cib, xmlNode **output_data, int call_options);
     int (*query) (cib_t *cib, const char *section, xmlNode **output_data,
                   int call_options);
     int (*query_from) (cib_t *cib, const char *host, const char *section,
                        xmlNode **output_data, int call_options);
 
     //! \deprecated This method will be removed and should not be used
     int (*is_master) (cib_t *cib);
 
     //! \deprecated Use the set_primary() method instead
     int (*set_master) (cib_t *cib, int call_options);
 
     //! \deprecated Use the set_secondary() method instead
     int (*set_slave) (cib_t *cib, int call_options);
 
     //! \deprecated This method will be removed and should not be used
     int (*set_slave_all) (cib_t *cib, int call_options);
 
     int (*sync) (cib_t *cib, const char *section, int call_options);
     int (*sync_from) (cib_t *cib, const char *host, const char *section,
                       int call_options);
     int (*upgrade) (cib_t *cib, int call_options);
     int (*bump_epoch) (cib_t *cib, int call_options);
+
+    /*!
+     * The \c <failed> element in the reply to a failed creation call is
+     * deprecated since 2.1.8.
+     */
     int (*create) (cib_t *cib, const char *section, xmlNode *data,
                    int call_options);
     int (*modify) (cib_t *cib, const char *section, xmlNode *data,
                    int call_options);
 
     //! \deprecated Use the \p modify() method instead
     int (*update) (cib_t *cib, const char *section, xmlNode *data,
                    int call_options);
 
     int (*replace) (cib_t *cib, const char *section, xmlNode *data,
                     int call_options);
     int (*remove) (cib_t *cib, const char *section, xmlNode *data,
                    int call_options);
     int (*erase) (cib_t *cib, xmlNode **output_data, int call_options);
 
     //! \deprecated This method does nothing and should not be called
     int (*delete_absolute) (cib_t *cib, const char *section, xmlNode *data,
                             int call_options);
 
     //! \deprecated This method is not implemented and should not be used
     int (*quit) (cib_t *cib, int call_options);
 
     int (*register_notification) (cib_t *cib, const char *callback,
                                   int enabled);
     gboolean (*register_callback) (cib_t *cib, int call_id, int timeout,
                                    gboolean only_success, void *user_data,
                                    const char *callback_name,
                                    void (*callback) (xmlNode*, int, int,
                                                      xmlNode*, void *));
     gboolean (*register_callback_full)(cib_t *cib, int call_id, int timeout,
                                        gboolean only_success, void *user_data,
                                        const char *callback_name,
                                        void (*callback)(xmlNode *, int, int,
                                                         xmlNode *, void *),
                                        void (*free_func)(void *));
 
     /*!
      * \brief Set the local CIB manager as the cluster's primary instance
      *
      * \param[in,out] cib           CIB connection
      * \param[in]     call_options  Group of enum cib_call_options flags
      *
      * \return Legacy Pacemaker return code (in particular, pcmk_ok on success)
      */
     int (*set_primary)(cib_t *cib, int call_options);
 
     /*!
      * \brief Set the local CIB manager as a secondary instance
      *
      * \param[in,out] cib           CIB connection
      * \param[in]     call_options  Group of enum cib_call_options flags
      *
      * \return Legacy Pacemaker return code (in particular, pcmk_ok on success)
      */
     int (*set_secondary)(cib_t *cib, int call_options);
 
     /*!
      * \brief Get the given CIB connection's unique client identifier(s)
      *
      * These can be used to check whether this client requested the action that
      * triggered a CIB notification.
      *
      * \param[in]  cib       CIB connection
      * \param[out] async_id  If not \p NULL, where to store asynchronous client
      *                       ID
      * \param[out] sync_id   If not \p NULL, where to store synchronous client
      *                       ID
      *
      * \return Legacy Pacemaker return code
      *
      * \note Some variants may have only one client for both asynchronous and
      *       synchronous requests.
      */
     int (*client_id)(const cib_t *cib, const char **async_id,
                      const char **sync_id);
 
     /*!
      * \brief Initiate an atomic CIB transaction for this client
      *
      * If the client has initiated a transaction and a new request's call
      * options contain \p cib_transaction, the new request is appended to the
      * transaction for later processing.
      *
      * Supported requests are those that meet the following conditions:
      * * can be processed synchronously (with any changes applied to a working
      *   CIB copy)
      * * are not queries
      * * do not involve other nodes
      * * do not affect the state of pacemaker-based itself
      *
      * Currently supported CIB API functions include:
      * * \p bump_epoch()
      * * \p create()
      * * \p erase()
      * * \p modify()
      * * \p remove()
      * * \p replace()
      * * \p upgrade()
      *
      * Because the transaction is atomic, individual requests do not trigger
      * callbacks or notifications when they are processed, and they do not
      * receive output XML. The commit request itself can trigger callbacks and
      * notifications if any are registered.
      *
      * An \c init_transaction() call is always synchronous.
      *
      * \param[in,out] cib           CIB connection
      *
      * \return Legacy Pacemaker return code
      */
     int (*init_transaction)(cib_t *cib);
 
     /*!
      * \brief End and optionally commit this client's CIB transaction
      *
      * When a client commits a transaction, all requests in the transaction are
      * processed in a FIFO manner until either a request fails or all requests
      * have been processed. Changes are applied to a working copy of the CIB.
      * If a request fails, the transaction and working CIB copy are discarded,
      * and an error is returned. If all requests succeed, the working CIB copy
      * replaces the initial CIB copy.
      *
      * Callbacks and notifications can be triggered by the commit request itself
      * but not by the individual requests in a transaction.
      *
      * An \c end_transaction() call with \p commit set to \c false is always
      * synchronous.
      *
      * \param[in,out] cib           CIB connection
      * \param[in]     commit        If \p true, commit transaction; otherwise,
      *                              discard it
      * \param[in]     call_options  Group of <tt>enum cib_call_options</tt>
      *                              flags
      *
      * \return Legacy Pacemaker return code
      */
     int (*end_transaction)(cib_t *cib, bool commit, int call_options);
 
     /*!
      * \brief Set the user as whom all CIB requests via methods will be executed
      *
      * By default, the value of the \c CIB_user environment variable is used if
      * set. Otherwise, the current effective user is used.
      *
      * \param[in,out] cib   CIB connection
      * \param[in]     user  Name of user whose permissions to use when
      *                      processing requests
      */
     void (*set_user)(cib_t *cib, const char *user);
 
     int (*fetch_schemas)(cib_t *cib, xmlNode **output_data, const char *after_ver,
                          int call_options);
 } cib_api_operations_t;
 
 struct cib_s {
     enum cib_state state;
     enum cib_conn_type type;
     enum cib_variant variant;
 
     int call_id;
     int call_timeout;
     void *variant_opaque;
     void *delegate_fn;
 
     GList *notify_list;
 
     //! \deprecated This method will be removed in a future release
     void (*op_callback) (const xmlNode *msg, int call_id, int rc,
                          xmlNode *output);
 
     cib_api_operations_t *cmds;
 
     xmlNode *transaction;
 
     char *user;
 };
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif // PCMK__CRM_CIB_CIB_TYPES__H
diff --git a/include/crm/common/xml_internal.h b/include/crm/common/xml_internal.h
index c70e95a9de..43b5f9f041 100644
--- a/include/crm/common/xml_internal.h
+++ b/include/crm/common/xml_internal.h
@@ -1,575 +1,579 @@
 /*
  * Copyright 2017-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef PCMK__XML_INTERNAL__H
 #  define PCMK__XML_INTERNAL__H
 
 /*
  * Internal-only wrappers for and extensions to libxml2 (libxslt)
  */
 
 #  include <stdlib.h>
 #  include <stdio.h>
 #  include <string.h>
 
 #  include <crm/crm.h>  /* transitively imports qblog.h */
 #  include <crm/common/output_internal.h>
 #  include <crm/common/xml_io_internal.h>
 #  include <crm/common/xml_names_internal.h>    // PCMK__XE_PROMOTABLE_LEGACY
 
 #  include <libxml/relaxng.h>
 
 /*!
  * \brief Base for directing lib{xml2,xslt} log into standard libqb backend
  *
  * This macro implements the core of what can be needed for directing
  * libxml2 or libxslt error messaging into standard, preconfigured
  * libqb-backed log stream.
  *
  * It's a bit unfortunate that libxml2 (and more sparsely, also libxslt)
  * emits a single message by chunks (location is emitted separatedly from
  * the message itself), so we have to take the effort to combine these
  * chunks back to single message.  Whether to do this or not is driven
  * with \p dechunk toggle.
  *
  * The form of a macro was chosen for implicit deriving of __FILE__, etc.
  * and also because static dechunking buffer should be differentiated per
  * library (here we assume different functions referring to this macro
  * will not ever be using both at once), preferably also per-library
  * context of use to avoid clashes altogether.
  *
  * Note that we cannot use qb_logt, because callsite data have to be known
  * at the moment of compilation, which it is not always the case -- xml_log
  * (and unfortunately there's no clear explanation of the fail to compile).
  *
  * Also note that there's no explicit guard against said libraries producing
  * never-newline-terminated chunks (which would just keep consuming memory),
  * as it's quite improbable.  Termination of the program in between the
  * same-message chunks will raise a flag with valgrind and the likes, though.
  *
  * And lastly, regarding how dechunking combines with other non-message
  * parameters -- for \p priority, most important running specification
  * wins (possibly elevated to LOG_ERR in case of nonconformance with the
  * newline-termination "protocol"), \p dechunk is expected to always be
  * on once it was at the start, and the rest (\p postemit and \p prefix)
  * are picked directly from the last chunk entry finalizing the message
  * (also reasonable to always have it the same with all related entries).
  *
  * \param[in] priority Syslog priority for the message to be logged
  * \param[in] dechunk  Whether to dechunk new-line terminated message
  * \param[in] postemit Code to be executed once message is sent out
  * \param[in] prefix   How to prefix the message or NULL for raw passing
  * \param[in] fmt      Format string as with printf-like functions
  * \param[in] ap       Variable argument list to supplement \p fmt format string
  */
 #define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)        \
 do {                                                                            \
     if (!(dechunk) && (prefix) == NULL) {  /* quick pass */                     \
         qb_log_from_external_source_va(__func__, __FILE__, (fmt),               \
                                        (priority), __LINE__, 0, (ap));          \
         (void) (postemit);                                                      \
     } else {                                                                    \
         int CXLB_len = 0;                                                       \
         char *CXLB_buf = NULL;                                                  \
         static int CXLB_buffer_len = 0;                                         \
         static char *CXLB_buffer = NULL;                                        \
         static uint8_t CXLB_priority = 0;                                       \
                                                                                 \
         CXLB_len = vasprintf(&CXLB_buf, (fmt), (ap));                           \
                                                                                 \
         if (CXLB_len <= 0 || CXLB_buf[CXLB_len - 1] == '\n' || !(dechunk)) {    \
             if (CXLB_len < 0) {                                                 \
                 CXLB_buf = (char *) "LOG CORRUPTION HAZARD"; /*we don't modify*/\
                 CXLB_priority = QB_MIN(CXLB_priority, LOG_ERR);                 \
             } else if (CXLB_len > 0 /* && (dechunk) */                          \
                        && CXLB_buf[CXLB_len - 1] == '\n') {                     \
                 CXLB_buf[CXLB_len - 1] = '\0';                                  \
             }                                                                   \
             if (CXLB_buffer) {                                                  \
                 qb_log_from_external_source(__func__, __FILE__, "%s%s%s",       \
                                             CXLB_priority, __LINE__, 0,         \
                                             (prefix) != NULL ? (prefix) : "",   \
                                             CXLB_buffer, CXLB_buf);             \
                 free(CXLB_buffer);                                              \
             } else {                                                            \
                 qb_log_from_external_source(__func__, __FILE__, "%s%s",         \
                                             (priority), __LINE__, 0,            \
                                             (prefix) != NULL ? (prefix) : "",   \
                                             CXLB_buf);                          \
             }                                                                   \
             if (CXLB_len < 0) {                                                 \
                 CXLB_buf = NULL;  /* restore temporary override */              \
             }                                                                   \
             CXLB_buffer = NULL;                                                 \
             CXLB_buffer_len = 0;                                                \
             (void) (postemit);                                                  \
                                                                                 \
         } else if (CXLB_buffer == NULL) {                                       \
             CXLB_buffer_len = CXLB_len;                                         \
             CXLB_buffer = CXLB_buf;                                             \
             CXLB_buf = NULL;                                                    \
             CXLB_priority = (priority);  /* remember as a running severest */   \
                                                                                 \
         } else {                                                                \
             CXLB_buffer = realloc(CXLB_buffer, 1 + CXLB_buffer_len + CXLB_len); \
             memcpy(CXLB_buffer + CXLB_buffer_len, CXLB_buf, CXLB_len);          \
             CXLB_buffer_len += CXLB_len;                                        \
             CXLB_buffer[CXLB_buffer_len] = '\0';                                \
             CXLB_priority = QB_MIN(CXLB_priority, (priority));  /* severest? */ \
         }                                                                       \
         free(CXLB_buf);                                                         \
     }                                                                           \
 } while (0)
 
 /*
  * \enum pcmk__xml_fmt_options
  * \brief Bit flags to control format in XML logs and dumps
  */
 enum pcmk__xml_fmt_options {
     //! Exclude certain XML attributes (for calculating digests)
     pcmk__xml_fmt_filtered   = (1 << 0),
 
     //! Include indentation and newlines
     pcmk__xml_fmt_pretty     = (1 << 1),
 
     //! Include the opening tag of an XML element, and include XML comments
     pcmk__xml_fmt_open       = (1 << 3),
 
     //! Include the children of an XML element
     pcmk__xml_fmt_children   = (1 << 4),
 
     //! Include the closing tag of an XML element
     pcmk__xml_fmt_close      = (1 << 5),
 
     // @COMPAT Can we start including text nodes unconditionally?
     //! Include XML text nodes
     pcmk__xml_fmt_text       = (1 << 6),
 
     // @COMPAT Remove when v1 patchsets are removed
     //! Log a created XML subtree
     pcmk__xml_fmt_diff_plus  = (1 << 7),
 
     // @COMPAT Remove when v1 patchsets are removed
     //! Log a removed XML subtree
     pcmk__xml_fmt_diff_minus = (1 << 8),
 
     // @COMPAT Remove when v1 patchsets are removed
     //! Log a minimal version of an XML diff (only showing the changes)
     pcmk__xml_fmt_diff_short = (1 << 9),
 };
 
 int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
                    int depth, uint32_t options);
 int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml);
 
 /* XML search strings for guest, remote and pacemaker_remote nodes */
 
 /* search string to find CIB resources entries for cluster nodes */
 #define PCMK__XP_MEMBER_NODE_CONFIG                                 \
     "//" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION "/" PCMK_XE_NODES    \
     "/" PCMK_XE_NODE "[not(@type) or @type='member']"
 
 /* search string to find CIB resources entries for guest nodes */
 #define PCMK__XP_GUEST_NODE_CONFIG \
     "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE  \
     "//" PCMK_XE_META_ATTRIBUTES "//" PCMK_XE_NVPAIR                    \
     "[@name='" PCMK_META_REMOTE_NODE "']"
 
 /* search string to find CIB resources entries for remote nodes */
 #define PCMK__XP_REMOTE_NODE_CONFIG                                     \
     "//" PCMK_XE_CIB "//" PCMK_XE_CONFIGURATION "//" PCMK_XE_PRIMITIVE  \
     "[@type='remote'][@provider='pacemaker']"
 
 /* search string to find CIB node status entries for pacemaker_remote nodes */
 #define PCMK__XP_REMOTE_NODE_STATUS                                 \
     "//" PCMK_XE_CIB "//" PCMK_XE_STATUS "//" PCMK__XE_NODE_STATE   \
     "[@" PCMK_XA_REMOTE_NODE "='true']"
 /*!
  * \internal
  * \brief Serialize XML (using libxml) into provided descriptor
  *
  * \param[in] fd  File descriptor to (piece-wise) write to
  * \param[in] cur XML subtree to proceed
  * 
  * \return a standard Pacemaker return code
  */
 int pcmk__xml2fd(int fd, xmlNode *cur);
 
 enum pcmk__xml_artefact_ns {
     pcmk__xml_artefact_ns_legacy_rng = 1,
     pcmk__xml_artefact_ns_legacy_xslt,
     pcmk__xml_artefact_ns_base_rng,
     pcmk__xml_artefact_ns_base_xslt,
 };
 
 void pcmk__strip_xml_text(xmlNode *xml);
 const char *pcmk__xe_add_last_written(xmlNode *xe);
 
 xmlNode *pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
                               const char *attr_n, const char *attr_v);
 
 
 void pcmk__xe_remove_attr(xmlNode *element, const char *name);
+bool pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data);
 void pcmk__xe_remove_matching_attrs(xmlNode *element,
                                     bool (*match)(xmlAttrPtr, void *),
                                     void *user_data);
 int pcmk__xe_delete_match(xmlNode *xml, xmlNode *search);
 int pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace);
 int pcmk__xe_update_match(xmlNode *xml, xmlNode *update);
 
 GString *pcmk__element_xpath(const xmlNode *xml);
 
 /*!
  * \internal
  * \enum pcmk__xml_escape_type
  * \brief Indicators of which XML characters to escape
  *
  * XML allows the escaping of special characters by replacing them with entity
  * references (for example, <tt>"&quot;"</tt>) or character references (for
  * example, <tt>"&#13;"</tt>).
  *
  * The special characters <tt>'&'</tt> (except as the beginning of an entity
  * reference) and <tt>'<'</tt> are not allowed in their literal forms in XML
  * character data. Character data is non-markup text (for example, the content
  * of a text node). <tt>'>'</tt> is allowed under most circumstances; we escape
  * it for safety and symmetry.
  *
  * For more details, see the "Character Data and Markup" section of the XML
  * spec, currently section 2.4:
  * https://www.w3.org/TR/xml/#dt-markup
  *
  * Attribute values are handled specially.
  * * If an attribute value is delimited by single quotes, then single quotes
  *   must be escaped within the value.
  * * Similarly, if an attribute value is delimited by double quotes, then double
  *   quotes must be escaped within the value.
  * * A conformant XML processor replaces a literal whitespace character (tab,
  *   newline, carriage return, space) in an attribute value with a space
  *   (\c '#x20') character. However, a reference to a whitespace character (for
  *   example, \c "&#x0A;" for \c '\n') does not get replaced.
  *   * For more details, see the "Attribute-Value Normalization" section of the
  *     XML spec, currently section 3.3.3. Note that the default attribute type
  *     is CDATA; we don't deal with NMTOKENS, etc.:
  *     https://www.w3.org/TR/xml/#AVNormalize
  *
  * Pacemaker always delimits attribute values with double quotes, so there's no
  * need to escape single quotes.
  *
  * Newlines and tabs should be escaped in attribute values when XML is
  * serialized to text, so that future parsing preserves them rather than
  * normalizing them to spaces.
  *
  * We always escape carriage returns, so that they're not converted to spaces
  * during attribute-value normalization and because displaying them as literals
  * is messy.
  */
 enum pcmk__xml_escape_type {
     /*!
      * For text nodes.
      * * Escape \c '<', \c '>', and \c '&' using entity references.
      * * Do not escape \c '\n' and \c '\t'.
      * * Escape other non-printing characters using character references.
      */
     pcmk__xml_escape_text,
 
     /*!
      * For attribute values.
      * * Escape \c '<', \c '>', \c '&', and \c '"' using entity references.
      * * Escape \c '\n', \c '\t', and other non-printing characters using
      *   character references.
      */
     pcmk__xml_escape_attr,
 
     /* @COMPAT Drop escaping of at least '\n' and '\t' for
      * pcmk__xml_escape_attr_pretty when openstack-info, openstack-floating-ip,
      * and openstack-virtual-ip resource agents no longer depend on it.
      *
      * At time of writing, openstack-info may set a multiline value for the
      * openstack_ports node attribute. The other two agents query the value and
      * require it to be on one line with no spaces.
      */
     /*!
      * For attribute values displayed in text output delimited by double quotes.
      * * Escape \c '\n' as \c "\\n"
      * * Escape \c '\r' as \c "\\r"
      * * Escape \c '\t' as \c "\\t"
      * * Escape \c '"' as \c "\\""
      */
     pcmk__xml_escape_attr_pretty,
 };
 
 bool pcmk__xml_needs_escape(const char *text, enum pcmk__xml_escape_type type);
 char *pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type);
 
 /*!
  * \internal
  * \brief Get the root directory to scan XML artefacts of given kind for
  *
  * \param[in] ns governs the hierarchy nesting against the inherent root dir
  *
  * \return root directory to scan XML artefacts of given kind for
  */
 char *
 pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns);
 
 /*!
  * \internal
  * \brief Get the fully unwrapped path to particular XML artifact (RNG/XSLT)
  *
  * \param[in] ns       denotes path forming details (parent dir, suffix)
  * \param[in] filespec symbolic file specification to be combined with
  *                     #artefact_ns to form the final path
  * \return unwrapped path to particular XML artifact (RNG/XSLT)
  */
 char *pcmk__xml_artefact_path(enum pcmk__xml_artefact_ns ns,
                               const char *filespec);
 
 /*!
  * \internal
  * \brief Retrieve the value of the \c PCMK_XA_ID XML attribute
  *
  * \param[in] xml  XML element to check
  *
  * \return Value of the \c PCMK_XA_ID attribute (may be \c NULL)
  */
 static inline const char *
 pcmk__xe_id(const xmlNode *xml)
 {
     return crm_element_value(xml, PCMK_XA_ID);
 }
 
 /*!
  * \internal
  * \brief Check whether an XML element is of a particular type
  *
  * \param[in] xml   XML element to compare
  * \param[in] name  XML element name to compare
  *
  * \return \c true if \p xml is of type \p name, otherwise \c false
  */
 static inline bool
 pcmk__xe_is(const xmlNode *xml, const char *name)
 {
     return (xml != NULL) && (xml->name != NULL) && (name != NULL)
            && (strcmp((const char *) xml->name, name) == 0);
 }
 
 /*!
  * \internal
  * \brief Return first non-text child node of an XML node
  *
  * \param[in] parent  XML node to check
  *
  * \return First non-text child node of \p parent (or NULL if none)
  */
 static inline xmlNode *
 pcmk__xml_first_child(const xmlNode *parent)
 {
     xmlNode *child = (parent? parent->children : NULL);
 
     while (child && (child->type == XML_TEXT_NODE)) {
         child = child->next;
     }
     return child;
 }
 
 /*!
  * \internal
  * \brief Return next non-text sibling node of an XML node
  *
  * \param[in] child  XML node to check
  *
  * \return Next non-text sibling of \p child (or NULL if none)
  */
 static inline xmlNode *
 pcmk__xml_next(const xmlNode *child)
 {
     xmlNode *next = (child? child->next : NULL);
 
     while (next && (next->type == XML_TEXT_NODE)) {
         next = next->next;
     }
     return next;
 }
 
 /*!
  * \internal
  * \brief Return next non-text sibling element of an XML element
  *
  * \param[in] child  XML element to check
  *
  * \return Next sibling element of \p child (or NULL if none)
  */
 static inline xmlNode *
 pcmk__xe_next(const xmlNode *child)
 {
     xmlNode *next = child? child->next : NULL;
 
     while (next && (next->type != XML_ELEMENT_NODE)) {
         next = next->next;
     }
     return next;
 }
 
 xmlNode *pcmk__xe_create(xmlNode *parent, const char *name);
 xmlNode *pcmk__xml_copy(xmlNode *parent, xmlNode *src);
 xmlNode *pcmk__xe_next_same(const xmlNode *node);
 
 void pcmk__xe_set_content(xmlNode *node, const char *format, ...)
     G_GNUC_PRINTF(2, 3);
 
 /*!
  * \internal
  * \brief Like pcmk__xe_set_props, but takes a va_list instead of
  *        arguments directly.
  *
  * \param[in,out] node   XML to add attributes to
  * \param[in]     pairs  NULL-terminated list of name/value pairs to add
  */
 void
 pcmk__xe_set_propv(xmlNodePtr node, va_list pairs);
 
 /*!
  * \internal
  * \brief Add a NULL-terminated list of name/value pairs to the given
  *        XML node as properties.
  *
  * \param[in,out] node XML node to add properties to
  * \param[in]     ...  NULL-terminated list of name/value pairs
  *
  * \note A NULL name terminates the arguments; a NULL value will be skipped.
  */
 void
 pcmk__xe_set_props(xmlNodePtr node, ...)
 G_GNUC_NULL_TERMINATED;
 
 /*!
  * \internal
  * \brief Get first attribute of an XML element
  *
  * \param[in] xe  XML element to check
  *
  * \return First attribute of \p xe (or NULL if \p xe is NULL or has none)
  */
 static inline xmlAttr *
 pcmk__xe_first_attr(const xmlNode *xe)
 {
     return (xe == NULL)? NULL : xe->properties;
 }
 
 /*!
  * \internal
  * \brief Extract the ID attribute from an XML element
  *
  * \param[in] xpath String to search
  * \param[in] node  Node to get the ID for
  *
  * \return ID attribute of \p node in xpath string \p xpath
  */
 char *
 pcmk__xpath_node_id(const char *xpath, const char *node);
 
 /*!
  * \internal
  * \brief Print an informational message if an xpath query returned multiple
  *        items with the same ID.
  *
  * \param[in,out] out       The output object
  * \param[in]     search    The xpath search result, most typically the result of
  *                          calling cib->cmds->query().
  * \param[in]     name      The name searched for
  */
 void
 pcmk__warn_multiple_name_matches(pcmk__output_t *out, xmlNode *search,
                                  const char *name);
 
 /* internal XML-related utilities */
 
 enum xml_private_flags {
      pcmk__xf_none        = 0x0000,
      pcmk__xf_dirty       = 0x0001,
      pcmk__xf_deleted     = 0x0002,
      pcmk__xf_created     = 0x0004,
      pcmk__xf_modified    = 0x0008,
 
      pcmk__xf_tracking    = 0x0010,
      pcmk__xf_processed   = 0x0020,
      pcmk__xf_skip        = 0x0040,
      pcmk__xf_moved       = 0x0080,
 
      pcmk__xf_acl_enabled = 0x0100,
      pcmk__xf_acl_read    = 0x0200,
      pcmk__xf_acl_write   = 0x0400,
      pcmk__xf_acl_deny    = 0x0800,
 
      pcmk__xf_acl_create  = 0x1000,
      pcmk__xf_acl_denied  = 0x2000,
      pcmk__xf_lazy        = 0x4000,
 };
 
 void pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag);
 
 /*!
  * \internal
  * \brief Iterate over child elements of \p xml
  *
  * This function iterates over the children of \p xml, performing the
  * callback function \p handler on each node.  If the callback returns
  * a value other than pcmk_rc_ok, the iteration stops and the value is
  * returned.  It is therefore possible that not all children will be
  * visited.
  *
  * \param[in,out] xml                 The starting XML node.  Can be NULL.
  * \param[in]     child_element_name  The name that the node must match in order
  *                                    for \p handler to be run.  If NULL, all
  *                                    child elements will match.
  * \param[in]     handler             The callback function.
  * \param[in,out] userdata            User data to pass to the callback function.
  *                                    Can be NULL.
  *
  * \return Standard Pacemaker return code
  */
 int
 pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
                        int (*handler)(xmlNode *xml, void *userdata),
                        void *userdata);
 
+bool pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
+                            void *user_data);
+
 static inline const char *
 pcmk__xml_attr_value(const xmlAttr *attr)
 {
     return ((attr == NULL) || (attr->children == NULL))? NULL
            : (const char *) attr->children->content;
 }
 
 
 gboolean pcmk__validate_xml(xmlNode *xml_blob, const char *validation,
                             xmlRelaxNGValidityErrorFunc error_handler, 
                             void *error_handler_context);
 
 void pcmk__log_known_schemas(void);
 const char *pcmk__remote_schema_dir(void);
 void pcmk__sort_schemas(void);
 
 // @COMPAT Remove when v1 patchsets are removed
 xmlNode *pcmk__diff_v1_xml_object(xmlNode *left, xmlNode *right, bool suppress);
 
 // @COMPAT Drop when PCMK__XE_PROMOTABLE_LEGACY is removed
 static inline const char *
 pcmk__map_element_name(const xmlNode *xml)
 {
     if (xml == NULL) {
         return NULL;
     } else if (pcmk__xe_is(xml, PCMK__XE_PROMOTABLE_LEGACY)) {
         return PCMK_XE_CLONE;
     } else {
         return (const char *) xml->name;
     }
 }
 
 #endif // PCMK__XML_INTERNAL__H
diff --git a/include/crm/common/xml_names_internal.h b/include/crm/common/xml_names_internal.h
index d8eb2f28d5..8989c0ef33 100644
--- a/include/crm/common/xml_names_internal.h
+++ b/include/crm/common/xml_names_internal.h
@@ -1,349 +1,351 @@
 /*
  * Copyright 2004-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
 #  define PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /*
  * XML element names used only by internal code
  */
 
 #define PCMK__XE_ACK                    "ack"
 #define PCMK__XE_ATTRIBUTES             "attributes"
 #define PCMK__XE_CIB_CALLBACK           "cib-callback"
 #define PCMK__XE_CIB_CALLDATA           "cib_calldata"
 #define PCMK__XE_CIB_COMMAND            "cib_command"
 #define PCMK__XE_CIB_REPLY              "cib-reply"
 #define PCMK__XE_CIB_RESULT             "cib_result"
 #define PCMK__XE_CIB_TRANSACTION        "cib_transaction"
 #define PCMK__XE_CIB_UPDATE_RESULT      "cib_update_result"
 #define PCMK__XE_COPY                   "copy"
 #define PCMK__XE_CRM_EVENT              "crm_event"
 #define PCMK__XE_CRM_XML                "crm_xml"
 #define PCMK__XE_DIV                    "div"
 #define PCMK__XE_DOWNED                 "downed"
 #define PCMK__XE_EXIT_NOTIFICATION      "exit-notification"
-#define PCMK__XE_FAILED                 "failed"
 #define PCMK__XE_FAILED_UPDATE          "failed_update"
 #define PCMK__XE_GENERATION_TUPLE       "generation_tuple"
 #define PCMK__XE_LRM                    "lrm"
 #define PCMK__XE_LRM_RESOURCE           "lrm_resource"
 #define PCMK__XE_LRM_RESOURCES          "lrm_resources"
 #define PCMK__XE_LRM_RSC_OP             "lrm_rsc_op"
 #define PCMK__XE_LRMD_ALERT             "lrmd_alert"
 #define PCMK__XE_LRMD_CALLDATA          "lrmd_calldata"
 #define PCMK__XE_LRMD_COMMAND           "lrmd_command"
 #define PCMK__XE_LRMD_IPC_MSG           "lrmd_ipc_msg"
 #define PCMK__XE_LRMD_IPC_PROXY         "lrmd_ipc_proxy"
 #define PCMK__XE_LRMD_NOTIFY            "lrmd_notify"
 #define PCMK__XE_LRMD_REPLY             "lrmd_reply"
 #define PCMK__XE_LRMD_RSC               "lrmd_rsc"
 #define PCMK__XE_LRMD_RSC_OP            "lrmd_rsc_op"
 #define PCMK__XE_MAINTENANCE            "maintenance"
 #define PCMK__XE_META                   "meta"
 #define PCMK__XE_NACK                   "nack"
 #define PCMK__XE_NODE_STATE             "node_state"
 #define PCMK__XE_NOTIFY                 "notify"
 #define PCMK__XE_OPTIONS                "options"
 #define PCMK__XE_PARAM                  "param"
 #define PCMK__XE_PING                   "ping"
 #define PCMK__XE_PING_RESPONSE          "ping_response"
 #define PCMK__XE_PSEUDO_EVENT           "pseudo_event"
 #define PCMK__XE_RSC_OP                 "rsc_op"
 #define PCMK__XE_SHUTDOWN               "shutdown"
 #define PCMK__XE_SPAN                   "span"
 #define PCMK__XE_ST_ASYNC_TIMEOUT_VALUE "st-async-timeout-value"
 #define PCMK__XE_ST_CALLDATA            "st_calldata"
 #define PCMK__XE_ST_DEVICE_ACTION       "st_device_action"
 #define PCMK__XE_ST_DEVICE_ID           "st_device_id"
 #define PCMK__XE_ST_HISTORY             "st_history"
 #define PCMK__XE_ST_NOTIFY_FENCE        "st_notify_fence"
 #define PCMK__XE_ST_REPLY               "st-reply"
 #define PCMK__XE_STONITH_COMMAND        "stonith_command"
 #define PCMK__XE_TICKET_STATE           "ticket_state"
 #define PCMK__XE_TRANSIENT_ATTRIBUTES   "transient_attributes"
 #define PCMK__XE_TRANSITION_GRAPH       "transition_graph"
 #define PCMK__XE_XPATH_QUERY            "xpath-query"
 #define PCMK__XE_XPATH_QUERY_PATH       "xpath-query-path"
 
 // @COMPAT Deprecated since 1.1.12
 #define PCMK__XE_ACL_USER               "acl_user"
 
 /* @COMPAT Deprecate somehow. It's undocumented and behaves the same as
  * PCMK__XE_CIB in places where it's recognized.
  */
 #define PCMK__XE_ALL                    "all"
 
 // @COMPAT Deprecated since 2.1.8
 #define PCMK__XE_CIB_GENERATION         "cib_generation"
 
 // @COMPAT Deprecated since 2.1.8
 #define PCMK__XE_CIB_UPDATE             "cib_update"
 
 // @COMPAT Deprecated since 2.1.7
 #define PCMK__XE_DIFF_ADDED             "diff-added"
 
 // @COMPAT Deprecated since 2.1.7
 #define PCMK__XE_DIFF_REMOVED           "diff-removed"
 
+// @COMPAT Deprecated since 2.1.8
+#define PCMK__XE_FAILED                 "failed"
+
 // @COMPAT Deprecated since 1.0.8 (commit 4cb100f)
 #define PCMK__XE_LIFETIME               "lifetime"
 
 /* @COMPAT Deprecated since 2.0.0; alias for <clone> with PCMK_META_PROMOTABLE
  * set to "true"
  */
 #define PCMK__XE_PROMOTABLE_LEGACY      "master"
 
 // @COMPAT Support for rkt is deprecated since 2.1.8
 #define PCMK__XE_RKT                    "rkt"
 
 // @COMPAT Deprecated since 1.1.12
 #define PCMK__XE_ROLE_REF               "role_ref"
 
 
 /*
  * XML attribute names used only by internal code
  */
 
 #define PCMK__XA_ATTR_CLEAR_INTERVAL    "attr_clear_interval"
 #define PCMK__XA_ATTR_CLEAR_OPERATION   "attr_clear_operation"
 #define PCMK__XA_ATTR_DAMPENING         "attr_dampening"
 #define PCMK__XA_ATTR_HOST              "attr_host"
 #define PCMK__XA_ATTR_HOST_ID           "attr_host_id"
 #define PCMK__XA_ATTR_IS_PRIVATE        "attr_is_private"
 #define PCMK__XA_ATTR_IS_REMOTE         "attr_is_remote"
 #define PCMK__XA_ATTR_NAME              "attr_name"
 #define PCMK__XA_ATTR_REGEX             "attr_regex"
 #define PCMK__XA_ATTR_RESOURCE          "attr_resource"
 #define PCMK__XA_ATTR_SECTION           "attr_section"
 #define PCMK__XA_ATTR_SET               "attr_set"
 #define PCMK__XA_ATTR_SET_TYPE          "attr_set_type"
 #define PCMK__XA_ATTR_SYNC_POINT        "attr_sync_point"
 #define PCMK__XA_ATTR_USER              "attr_user"
 #define PCMK__XA_ATTR_VALUE             "attr_value"
 #define PCMK__XA_ATTR_VERSION           "attr_version"
 #define PCMK__XA_ATTR_WRITER            "attr_writer"
 #define PCMK__XA_ATTRD_IS_FORCE_WRITE   "attrd_is_force_write"
 #define PCMK__XA_CALL_ID                "call-id"
 #define PCMK__XA_CIB_CALLID             "cib_callid"
 #define PCMK__XA_CIB_CALLOPT            "cib_callopt"
 #define PCMK__XA_CIB_CLIENTID           "cib_clientid"
 #define PCMK__XA_CIB_CLIENTNAME         "cib_clientname"
 #define PCMK__XA_CIB_DELEGATED_FROM     "cib_delegated_from"
 #define PCMK__XA_CIB_HOST               "cib_host"
 #define PCMK__XA_CIB_ISREPLYTO          "cib_isreplyto"
 #define PCMK__XA_CIB_NOTIFY_ACTIVATE    "cib_notify_activate"
 #define PCMK__XA_CIB_NOTIFY_TYPE        "cib_notify_type"
 #define PCMK__XA_CIB_OP                 "cib_op"
 #define PCMK__XA_CIB_PING_ID            "cib_ping_id"
 #define PCMK__XA_CIB_RC                 "cib_rc"
 #define PCMK__XA_CIB_SCHEMA_MAX         "cib_schema_max"
 #define PCMK__XA_CIB_SECTION            "cib_section"
 #define PCMK__XA_CIB_UPDATE             "cib_update"
 #define PCMK__XA_CIB_UPGRADE_RC         "cib_upgrade_rc"
 #define PCMK__XA_CIB_USER               "cib_user"
 #define PCMK__XA_CLIENT_NAME            "client_name"
 #define PCMK__XA_CLIENT_UUID            "client_uuid"
 #define PCMK__XA_CONFIG_ERRORS          "config-errors"
 #define PCMK__XA_CONFIG_WARNINGS        "config-warnings"
 #define PCMK__XA_CONFIRM                "confirm"
 #define PCMK__XA_CONNECTION_HOST        "connection_host"
 #define PCMK__XA_CONTENT                "content"
 #define PCMK__XA_CRMD_STATE             "crmd_state"
 #define PCMK__XA_CRM_HOST_TO            "crm_host_to"
 #define PCMK__XA_CRM_LIMIT_MAX          "crm-limit-max"
 #define PCMK__XA_CRM_LIMIT_MODE         "crm-limit-mode"
 #define PCMK__XA_CRM_SUBSYSTEM          "crm_subsystem"
 #define PCMK__XA_CRM_SYS_FROM           "crm_sys_from"
 #define PCMK__XA_CRM_SYS_TO             "crm_sys_to"
 #define PCMK__XA_CRM_TASK               "crm_task"
 #define PCMK__XA_CRM_TGRAPH_IN          "crm-tgraph-in"
 #define PCMK__XA_CRM_USER               "crm_user"
 #define PCMK__XA_DC_LEAVING             "dc-leaving"
 #define PCMK__XA_DIGEST                 "digest"
 #define PCMK__XA_ELECTION_AGE_SEC       "election-age-sec"
 #define PCMK__XA_ELECTION_AGE_NANO_SEC  "election-age-nano-sec"
 #define PCMK__XA_ELECTION_ID            "election-id"
 #define PCMK__XA_ELECTION_OWNER         "election-owner"
 #define PCMK__XA_GRANTED                "granted"
 #define PCMK__XA_GRAPH_ERRORS           "graph-errors"
 #define PCMK__XA_GRAPH_WARNINGS         "graph-warnings"
 #define PCMK__XA_HIDDEN                 "hidden"
 #define PCMK__XA_HTTP_EQUIV             "http-equiv"
 #define PCMK__XA_IN_CCM                 "in_ccm"
 #define PCMK__XA_JOIN                   "join"
 #define PCMK__XA_JOIN_ID                "join_id"
 #define PCMK__XA_LINE                   "line"
 #define PCMK__XA_LONG_ID                "long-id"
 #define PCMK__XA_LRMD_ALERT_ID          "lrmd_alert_id"
 #define PCMK__XA_LRMD_ALERT_PATH        "lrmd_alert_path"
 #define PCMK__XA_LRMD_CALLID            "lrmd_callid"
 #define PCMK__XA_LRMD_CALLOPT           "lrmd_callopt"
 #define PCMK__XA_LRMD_CLASS             "lrmd_class"
 #define PCMK__XA_LRMD_CLIENTID          "lrmd_clientid"
 #define PCMK__XA_LRMD_CLIENTNAME        "lrmd_clientname"
 #define PCMK__XA_LRMD_EXEC_OP_STATUS    "lrmd_exec_op_status"
 #define PCMK__XA_LRMD_EXEC_RC           "lrmd_exec_rc"
 #define PCMK__XA_LRMD_EXEC_TIME         "lrmd_exec_time"
 #define PCMK__XA_LRMD_IPC_CLIENT        "lrmd_ipc_client"
 #define PCMK__XA_LRMD_IPC_MSG_FLAGS     "lrmd_ipc_msg_flags"
 #define PCMK__XA_LRMD_IPC_MSG_ID        "lrmd_ipc_msg_id"
 #define PCMK__XA_LRMD_IPC_OP            "lrmd_ipc_op"
 #define PCMK__XA_LRMD_IPC_SERVER        "lrmd_ipc_server"
 #define PCMK__XA_LRMD_IPC_SESSION       "lrmd_ipc_session"
 #define PCMK__XA_LRMD_IPC_USER          "lrmd_ipc_user"
 #define PCMK__XA_LRMD_IS_IPC_PROVIDER   "lrmd_is_ipc_provider"
 #define PCMK__XA_LRMD_OP                "lrmd_op"
 #define PCMK__XA_LRMD_ORIGIN            "lrmd_origin"
 #define PCMK__XA_LRMD_PROTOCOL_VERSION  "lrmd_protocol_version"
 #define PCMK__XA_LRMD_PROVIDER          "lrmd_provider"
 #define PCMK__XA_LRMD_QUEUE_TIME        "lrmd_queue_time"
 #define PCMK__XA_LRMD_RC                "lrmd_rc"
 #define PCMK__XA_LRMD_RCCHANGE_TIME     "lrmd_rcchange_time"
 #define PCMK__XA_LRMD_REMOTE_MSG_ID     "lrmd_remote_msg_id"
 #define PCMK__XA_LRMD_REMOTE_MSG_TYPE   "lrmd_remote_msg_type"
 #define PCMK__XA_LRMD_RSC_ACTION        "lrmd_rsc_action"
 #define PCMK__XA_LRMD_RSC_DELETED       "lrmd_rsc_deleted"
 #define PCMK__XA_LRMD_RSC_EXIT_REASON   "lrmd_rsc_exit_reason"
 #define PCMK__XA_LRMD_RSC_ID            "lrmd_rsc_id"
 #define PCMK__XA_LRMD_RSC_INTERVAL      "lrmd_rsc_interval"
 #define PCMK__XA_LRMD_RSC_OUTPUT        "lrmd_rsc_output"
 #define PCMK__XA_LRMD_RSC_START_DELAY   "lrmd_rsc_start_delay"
 #define PCMK__XA_LRMD_RSC_USERDATA_STR  "lrmd_rsc_userdata_str"
 #define PCMK__XA_LRMD_RUN_TIME          "lrmd_run_time"
 #define PCMK__XA_LRMD_TIMEOUT           "lrmd_timeout"
 #define PCMK__XA_LRMD_TYPE              "lrmd_type"
 #define PCMK__XA_LRMD_WATCHDOG          "lrmd_watchdog"
 #define PCMK__XA_MAJOR_VERSION          "major_version"
 #define PCMK__XA_MINOR_VERSION          "minor_version"
 #define PCMK__XA_MODE                   "mode"
 #define PCMK__XA_MOON                   "moon"
 #define PCMK__XA_NAMESPACE              "namespace"
 #define PCMK__XA_NODE_FENCED            "node_fenced"
 #define PCMK__XA_NODE_IN_MAINTENANCE    "node_in_maintenance"
 #define PCMK__XA_NODE_START_STATE       "node_start_state"
 #define PCMK__XA_NODE_STATE             "node_state"
 #define PCMK__XA_OP_DIGEST              "op-digest"
 #define PCMK__XA_OP_FORCE_RESTART       "op-force-restart"
 #define PCMK__XA_OP_RESTART_DIGEST      "op-restart-digest"
 #define PCMK__XA_OP_SECURE_DIGEST       "op-secure-digest"
 #define PCMK__XA_OP_SECURE_PARAMS       "op-secure-params"
 #define PCMK__XA_OP_STATUS              "op-status"
 #define PCMK__XA_OPERATION_KEY          "operation_key"
 #define PCMK__XA_ORIGINAL_CIB_OP        "original_cib_op"
 #define PCMK__XA_PACEMAKERD_STATE       "pacemakerd_state"
 #define PCMK__XA_PASSWORD               "password"
 #define PCMK__XA_PRIORITY               "priority"
 #define PCMK__XA_RC_CODE                "rc-code"
 #define PCMK__XA_REAP                   "reap"
 
 /* Actions to be executed on Pacemaker Remote nodes are routed through the
  * controller on the cluster node hosting the remote connection. That cluster
  * node is considered the router node for the action.
  */
 #define PCMK__XA_ROUTER_NODE            "router_node"
 
 #define PCMK__XA_RSC_ID                 "rsc-id"
 #define PCMK__XA_RSC_PROVIDES           "rsc_provides"
 #define PCMK__XA_SCHEMA                 "schema"
 #define PCMK__XA_SCHEMAS                "schemas"
 #define PCMK__XA_SRC                    "src"
 #define PCMK__XA_ST_ACTION_DISALLOWED   "st_action_disallowed"
 #define PCMK__XA_ST_ACTION_TIMEOUT      "st_action_timeout"
 #define PCMK__XA_ST_AVAILABLE_DEVICES   "st-available-devices"
 #define PCMK__XA_ST_CALLID              "st_callid"
 #define PCMK__XA_ST_CALLOPT             "st_callopt"
 #define PCMK__XA_ST_CLIENTID            "st_clientid"
 #define PCMK__XA_ST_CLIENTNAME          "st_clientname"
 #define PCMK__XA_ST_CLIENTNODE          "st_clientnode"
 #define PCMK__XA_ST_DATE                "st_date"
 #define PCMK__XA_ST_DATE_NSEC           "st_date_nsec"
 #define PCMK__XA_ST_DELAY               "st_delay"
 #define PCMK__XA_ST_DELAY_BASE          "st_delay_base"
 #define PCMK__XA_ST_DELAY_MAX           "st_delay_max"
 #define PCMK__XA_ST_DELEGATE            "st_delegate"
 #define PCMK__XA_ST_DEVICE_ACTION       "st_device_action"
 #define PCMK__XA_ST_DEVICE_ID           "st_device_id"
 #define PCMK__XA_ST_DEVICE_SUPPORT_FLAGS    "st_device_support_flags"
 #define PCMK__XA_ST_DIFFERENTIAL        "st_differential"
 #define PCMK__XA_ST_MONITOR_VERIFIED    "st_monitor_verified"
 #define PCMK__XA_ST_NOTIFY_ACTIVATE     "st_notify_activate"
 #define PCMK__XA_ST_NOTIFY_DEACTIVATE   "st_notify_deactivate"
 #define PCMK__XA_ST_OP                  "st_op"
 #define PCMK__XA_ST_OP_MERGED           "st_op_merged"
 #define PCMK__XA_ST_ORIGIN              "st_origin"
 #define PCMK__XA_ST_OUTPUT              "st_output"
 #define PCMK__XA_ST_RC                  "st_rc"
 #define PCMK__XA_ST_REMOTE_OP           "st_remote_op"
 #define PCMK__XA_ST_REMOTE_OP_RELAY     "st_remote_op_relay"
 #define PCMK__XA_ST_REQUIRED            "st_required"
 #define PCMK__XA_ST_STATE               "st_state"
 #define PCMK__XA_ST_TARGET              "st_target"
 #define PCMK__XA_ST_TIMEOUT             "st_timeout"
 #define PCMK__XA_ST_TOLERANCE           "st_tolerance"
 #define PCMK__XA_SUBT                   "subt"                  // subtype
 #define PCMK__XA_T                      "t"                     // type
 #define PCMK__XA_TRANSITION_KEY         "transition-key"
 #define PCMK__XA_TRANSITION_MAGIC       "transition-magic"
 #define PCMK__XA_UPTIME                 "uptime"
 
 // @COMPAT Deprecated since 2.1.8
 #define PCMK__XA_CIB_OBJECT             "cib_object"
 
 // @COMPAT Deprecated since 2.1.8
 #define PCMK__XA_CIB_OBJECT_TYPE        "cib_object_type"
 
 // @COMPAT Deprecated since 1.1.12; used with legacy CIB updates
 #define PCMK__XA_CIB_LOCAL_NOTIFY_ID    "cib_local_notify_id"
 
 // @COMPAT Deprecated since 1.1.12; used with legacy CIB updates
 #define PCMK__XA_CIB_UPDATE_DIFF        "cib_update_diff"
 
 // @COMPAT Used only with v1 patchsets
 #define PCMK__XA_CRM_DIFF_MARKER        "__crm_diff_marker__"
 
 // @COMPAT Deprecated since 2.1.5
 #define PCMK__XA_FIRST_INSTANCE         "first-instance"
 
 // @COMPAT Deprecated since 2.1.7
 #define PCMK__XA_ORDERING               "ordering"
 
 // @COMPAT Deprecated alias for PCMK_XA_PROMOTED_MAX since 2.0.0
 #define PCMK__XA_PROMOTED_MAX_LEGACY    "masters"
 
 // @COMPAT Deprecated alias for PCMK_XA_PROMOTED_ONLY since 2.0.0
 #define PCMK__XA_PROMOTED_ONLY_LEGACY   "master_only"
 
 // @COMPAT Deprecated since 1.1.12
 #define PCMK__XA_REF                    "ref"
 
 // @COMPAT Deprecated since 2.1.6
 #define PCMK__XA_REPLACE                "replace"
 
 // @COMPAT Deprecated alias for \c PCMK_XA_AUTOMATIC since 1.1.14
 #define PCMK__XA_REQUIRED               "required"
 
 // @COMPAT Deprecated since 2.1.5
 #define PCMK__XA_RSC_INSTANCE           "rsc-instance"
 
 // @COMPAT Deprecated since 2.1.5
 #define PCMK__XA_THEN_INSTANCE          "then-instance"
 
 // @COMPAT Deprecated since 2.1.5
 #define PCMK__XA_WITH_RSC_INSTANCE      "with-rsc-instance"
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif // PCMK__CRM_COMMON_XML_NAMES_INTERNAL__H
diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c
index a70aeb1972..593d6c97fd 100644
--- a/lib/cib/cib_ops.c
+++ b/lib/cib/cib_ops.c
@@ -1,966 +1,904 @@
 /*
  * Copyright 2004-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <time.h>
 
 #include <sys/param.h>
 #include <sys/types.h>
 
 #include <glib.h>
 #include <libxml/tree.h>
 
 #include <crm/crm.h>
 #include <crm/cib/internal.h>
 
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>
 
 // @TODO: Free this via crm_exit() when libcib gets merged with libcrmcommon
 static GHashTable *operation_table = NULL;
 
 static const cib__operation_t cib_ops[] = {
     {
         PCMK__CIB_REQUEST_ABS_DELETE, cib__op_abs_delete,
         cib__op_attr_modifies|cib__op_attr_privileged
     },
     {
         PCMK__CIB_REQUEST_APPLY_PATCH, cib__op_apply_patch,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_BUMP, cib__op_bump,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_COMMIT_TRANSACT, cib__op_commit_transact,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_replaces
         |cib__op_attr_writes_through
     },
     {
         PCMK__CIB_REQUEST_CREATE, cib__op_create,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_DELETE, cib__op_delete,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_ERASE, cib__op_erase,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_replaces
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_IS_PRIMARY, cib__op_is_primary,
         cib__op_attr_privileged
     },
     {
         PCMK__CIB_REQUEST_MODIFY, cib__op_modify,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_NOOP, cib__op_noop, cib__op_attr_none
     },
     {
         CRM_OP_PING, cib__op_ping, cib__op_attr_none
     },
     {
         // @COMPAT: Drop cib__op_attr_modifies when we drop legacy mode support
         PCMK__CIB_REQUEST_PRIMARY, cib__op_primary,
         cib__op_attr_modifies|cib__op_attr_privileged|cib__op_attr_local
     },
     {
         PCMK__CIB_REQUEST_QUERY, cib__op_query, cib__op_attr_none
     },
     {
         PCMK__CIB_REQUEST_REPLACE, cib__op_replace,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_replaces
         |cib__op_attr_writes_through
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_SECONDARY, cib__op_secondary,
         cib__op_attr_privileged|cib__op_attr_local
     },
     {
         PCMK__CIB_REQUEST_SHUTDOWN, cib__op_shutdown, cib__op_attr_privileged
     },
     {
         PCMK__CIB_REQUEST_SYNC_TO_ALL, cib__op_sync_all, cib__op_attr_privileged
     },
     {
         PCMK__CIB_REQUEST_SYNC_TO_ONE, cib__op_sync_one, cib__op_attr_privileged
     },
     {
         PCMK__CIB_REQUEST_UPGRADE, cib__op_upgrade,
         cib__op_attr_modifies
         |cib__op_attr_privileged
         |cib__op_attr_writes_through
         |cib__op_attr_transaction
     },
     {
         PCMK__CIB_REQUEST_SCHEMAS, cib__op_schemas, cib__op_attr_local
     }
 };
 
 /*!
  * \internal
  * \brief Get the \c cib__operation_t object for a given CIB operation name
  *
  * \param[in]  op         CIB operation name
  * \param[out] operation  Where to store CIB operation object
  *
  * \return Standard Pacemaker return code
  */
 int
 cib__get_operation(const char *op, const cib__operation_t **operation)
 {
     CRM_ASSERT((op != NULL) && (operation != NULL));
 
     if (operation_table == NULL) {
         operation_table = pcmk__strkey_table(NULL, NULL);
 
         for (int lpc = 0; lpc < PCMK__NELEM(cib_ops); lpc++) {
             const cib__operation_t *oper = &(cib_ops[lpc]);
 
             g_hash_table_insert(operation_table, (gpointer) oper->name,
                                 (gpointer) oper);
         }
     }
 
     *operation = g_hash_table_lookup(operation_table, op);
     if (*operation == NULL) {
         crm_err("Operation %s is invalid", op);
         return EINVAL;
     }
     return pcmk_rc_ok;
 }
 
 int
 cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     xmlNode *obj_root = NULL;
     int result = pcmk_ok;
 
     crm_trace("Processing %s for %s section",
               op, pcmk__s(section, "unspecified"));
 
     if (options & cib_xpath) {
         return cib_process_xpath(op, options, section, req, input,
                                  existing_cib, result_cib, answer);
     }
 
     CRM_CHECK(*answer == NULL, free_xml(*answer));
     *answer = NULL;
 
     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
         section = NULL;
     }
 
     obj_root = pcmk_find_cib_element(existing_cib, section);
 
     if (obj_root == NULL) {
         result = -ENXIO;
 
     } else if (options & cib_no_children) {
         xmlNode *shallow = pcmk__xe_create(*answer,
                                            (const char *) obj_root->name);
 
         copy_in_properties(shallow, obj_root);
         *answer = shallow;
 
     } else {
         *answer = obj_root;
     }
 
     if (result == pcmk_ok && *answer == NULL) {
         crm_err("Error creating query response");
         result = -ENOMSG;
     }
 
     return result;
 }
 
 static int
 update_counter(xmlNode *xml_obj, const char *field, bool reset)
 {
     char *new_value = NULL;
     char *old_value = NULL;
     int int_value = -1;
 
     if (!reset && crm_element_value(xml_obj, field) != NULL) {
         old_value = crm_element_value_copy(xml_obj, field);
     }
     if (old_value != NULL) {
         int_value = atoi(old_value);
         new_value = pcmk__itoa(++int_value);
     } else {
         new_value = pcmk__str_copy("1");
     }
 
     crm_trace("Update %s from %s to %s",
               field, pcmk__s(old_value, "unset"), new_value);
     crm_xml_add(xml_obj, field, new_value);
 
     free(new_value);
     free(old_value);
 
     return pcmk_ok;
 }
 
 int
 cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                   xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     int result = pcmk_ok;
 
     crm_trace("Processing \"%s\" event", op);
 
     if (*result_cib != existing_cib) {
         free_xml(*result_cib);
     }
     *result_cib = createEmptyCib(0);
     copy_in_properties(*result_cib, existing_cib);
     update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
     *answer = NULL;
 
     return result;
 }
 
 int
 cib_process_upgrade(const char *op, int options, const char *section, xmlNode * req,
                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
                     xmlNode ** answer)
 {
     int rc = 0;
     int new_version = 0;
     int current_version = 0;
     int max_version = 0;
     const char *max = crm_element_value(req, PCMK__XA_CIB_SCHEMA_MAX);
     const char *value = crm_element_value(existing_cib, PCMK_XA_VALIDATE_WITH);
 
     *answer = NULL;
     crm_trace("Processing \"%s\" event with max=%s", op, max);
 
     if (value != NULL) {
         current_version = get_schema_version(value);
     }
 
     if (max) {
         max_version = get_schema_version(max);
     }
 
     rc = update_validation(result_cib, &new_version, max_version, TRUE,
                            !(options & cib_verbose));
     if (new_version > current_version) {
         update_counter(*result_cib, PCMK_XA_ADMIN_EPOCH, false);
         update_counter(*result_cib, PCMK_XA_EPOCH, true);
         update_counter(*result_cib, PCMK_XA_NUM_UPDATES, true);
         return pcmk_ok;
     }
 
     return rc;
 }
 
 int
 cib_process_bump(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     int result = pcmk_ok;
 
     crm_trace("Processing %s for epoch='%s'", op,
               pcmk__s(crm_element_value(existing_cib, PCMK_XA_EPOCH), ""));
 
     *answer = NULL;
     update_counter(*result_cib, PCMK_XA_EPOCH, false);
 
     return result;
 }
 
 int
 cib_process_replace(const char *op, int options, const char *section, xmlNode * req,
                     xmlNode * input, xmlNode * existing_cib, xmlNode ** result_cib,
                     xmlNode ** answer)
 {
     int result = pcmk_ok;
 
     crm_trace("Processing %s for %s section",
               op, pcmk__s(section, "unspecified"));
 
     if (options & cib_xpath) {
         return cib_process_xpath(op, options, section, req, input,
                                  existing_cib, result_cib, answer);
     }
 
     *answer = NULL;
 
     if (input == NULL) {
         return -EINVAL;
     }
 
     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
         section = NULL;
 
     } else if (pcmk__xe_is(input, section)) {
         section = NULL;
     }
 
     if (pcmk__xe_is(input, PCMK_XE_CIB)) {
         int updates = 0;
         int epoch = 0;
         int admin_epoch = 0;
 
         int replace_updates = 0;
         int replace_epoch = 0;
         int replace_admin_epoch = 0;
 
         const char *reason = NULL;
         const char *peer = crm_element_value(req, PCMK__XA_SRC);
         const char *digest = crm_element_value(req, PCMK__XA_DIGEST);
 
         if (digest) {
             const char *version = crm_element_value(req,
                                                     PCMK_XA_CRM_FEATURE_SET);
             char *digest_verify = calculate_xml_versioned_digest(input, FALSE, TRUE,
                                                                  version ? version :
                                                                  CRM_FEATURE_SET);
 
             if (!pcmk__str_eq(digest_verify, digest, pcmk__str_casei)) {
                 crm_err("Digest mis-match on replace from %s: %s vs. %s (expected)", peer,
                         digest_verify, digest);
                 reason = "digest mismatch";
 
             } else {
                 crm_info("Digest matched on replace from %s: %s", peer, digest);
             }
             free(digest_verify);
 
         } else {
             crm_trace("No digest to verify");
         }
 
         cib_version_details(existing_cib, &admin_epoch, &epoch, &updates);
         cib_version_details(input, &replace_admin_epoch, &replace_epoch, &replace_updates);
 
         if (replace_admin_epoch < admin_epoch) {
             reason = PCMK_XA_ADMIN_EPOCH;
 
         } else if (replace_admin_epoch > admin_epoch) {
             /* no more checks */
 
         } else if (replace_epoch < epoch) {
             reason = PCMK_XA_EPOCH;
 
         } else if (replace_epoch > epoch) {
             /* no more checks */
 
         } else if (replace_updates < updates) {
             reason = PCMK_XA_NUM_UPDATES;
         }
 
         if (reason != NULL) {
             crm_info("Replacement %d.%d.%d from %s not applied to %d.%d.%d:"
                      " current %s is greater than the replacement",
                      replace_admin_epoch, replace_epoch,
                      replace_updates, peer, admin_epoch, epoch, updates, reason);
             result = -pcmk_err_old_data;
         } else {
             crm_info("Replaced %d.%d.%d with %d.%d.%d from %s",
                      admin_epoch, epoch, updates,
                      replace_admin_epoch, replace_epoch, replace_updates, peer);
         }
 
         if (*result_cib != existing_cib) {
             free_xml(*result_cib);
         }
         *result_cib = pcmk__xml_copy(NULL, input);
 
     } else {
         xmlNode *obj_root = NULL;
 
         obj_root = pcmk_find_cib_element(*result_cib, section);
         result = pcmk__xe_replace_match(obj_root, input);
         result = pcmk_rc2legacy(result);
         if (result != pcmk_ok) {
             crm_trace("No matching object to replace");
         }
     }
 
     return result;
 }
 
 static int
 delete_child(xmlNode *child, void *userdata)
 {
     xmlNode *obj_root = userdata;
 
     if (pcmk__xe_delete_match(obj_root, child) != pcmk_rc_ok) {
         crm_trace("No matching object to delete: %s=%s",
                   child->name, pcmk__xe_id(child));
     }
 
     return pcmk_rc_ok;
 }
 
 int
 cib_process_delete(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     xmlNode *obj_root = NULL;
 
     crm_trace("Processing \"%s\" event", op);
 
     if (options & cib_xpath) {
         return cib_process_xpath(op, options, section, req, input,
                                  existing_cib, result_cib, answer);
     }
 
     if (input == NULL) {
         crm_err("Cannot perform modification with no data");
         return -EINVAL;
     }
 
     obj_root = pcmk_find_cib_element(*result_cib, section);
     if (pcmk__xe_is(input, section)) {
         pcmk__xe_foreach_child(input, NULL, delete_child, obj_root);
     } else {
         delete_child(input, obj_root);
     }
 
     return pcmk_ok;
 }
 
 int
 cib_process_modify(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     xmlNode *obj_root = NULL;
 
     crm_trace("Processing \"%s\" event", op);
 
     if (options & cib_xpath) {
         return cib_process_xpath(op, options, section, req, input,
                                  existing_cib, result_cib, answer);
     }
 
     if (input == NULL) {
         crm_err("Cannot perform modification with no data");
         return -EINVAL;
     }
 
     obj_root = pcmk_find_cib_element(*result_cib, section);
     if (obj_root == NULL) {
         xmlNode *tmp_section = NULL;
         const char *path = pcmk_cib_parent_name_for(section);
 
         if (path == NULL) {
             return -EINVAL;
         }
 
         tmp_section = pcmk__xe_create(NULL, section);
         cib_process_xpath(PCMK__CIB_REQUEST_CREATE, 0, path, NULL, tmp_section,
                           NULL, result_cib, answer);
         free_xml(tmp_section);
 
         obj_root = pcmk_find_cib_element(*result_cib, section);
     }
 
     CRM_CHECK(obj_root != NULL, return -EINVAL);
 
     if (pcmk__xe_update_match(obj_root, input) != pcmk_rc_ok) {
         if (options & cib_can_create) {
             pcmk__xml_copy(obj_root, input);
         } else {
             return -ENXIO;
         }
     }
 
     // @COMPAT cib_mixed_update is deprecated as of 2.1.7
     if (pcmk_is_set(options, cib_mixed_update)) {
         int max = 0, lpc;
         xmlXPathObjectPtr xpathObj = xpath_search(*result_cib, "//@__delete__");
 
         if (xpathObj) {
             max = numXpathResults(xpathObj);
             crm_log_xml_trace(*result_cib, "Mixed result");
         }
 
         for (lpc = 0; lpc < max; lpc++) {
             xmlNode *match = getXpathResult(xpathObj, lpc);
             xmlChar *match_path = xmlGetNodePath(match);
 
             crm_debug("Destroying %s", match_path);
             free(match_path);
             free_xml(match);
         }
 
         freeXpathObject(xpathObj);
     }
     return pcmk_ok;
 }
 
-static int
-update_cib_object(xmlNode *parent, xmlNode *update)
-{
-    int result = pcmk_ok;
-    const char *update_id = pcmk__xe_id(update);
-
-    xmlNode *target = pcmk__xe_create(parent, (const char *) update->name);
-
-    if (update_id != NULL) {
-        crm_trace("Processing update for <%s " PCMK_XA_ID "='%s'>",
-                  update->name, update_id);
-    } else {
-        crm_trace("Processing update for <%s>", update->name);
-    }
-
-    /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. When we can break
-     * behavioral backward compatibility, drop this and drop the definition of
-     * PCMK__XA_REPLACE.
-     */
-    pcmk__xe_remove_attr(update, PCMK__XA_REPLACE);
-
-    copy_in_properties(target, update);
-
-    if (xml_acl_denied(target)) {
-        crm_notice("Cannot update <%s " PCMK_XA_ID "=%s>",
-                   update->name, pcmk__s(update_id, "<null>"));
-        return -EACCES;
-    }
-
-    if (update_id != NULL) {
-        crm_trace("Processing children of <%s " PCMK_XA_ID "='%s'>",
-                  update->name, update_id);
-    } else {
-        crm_trace("Processing children of <%s>", update->name);
-    }
-
-    for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
-         child = pcmk__xml_next(child)) {
-
-        const char *child_id = pcmk__xe_id(child);
-        int tmp_result = 0;
-
-        if (child_id != NULL) {
-            crm_trace("Updating child <%s " PCMK_XA_ID "='%s'>",
-                      child->name, child_id);
-        } else {
-            crm_trace("Updating child <%s>", child->name);
-        }
-
-        tmp_result = update_cib_object(target, child);
-
-        /*  only the first error is likely to be interesting */
-        if (tmp_result != pcmk_ok) {
-            if (child_id != NULL) {
-                crm_err("Error updating child <%s " PCMK_XA_ID "='%s'>",
-                        child->name, child_id);
-            } else {
-                crm_err("Error updating child <%s>", child->name);
-            }
-
-            if (result == pcmk_ok) {
-                result = tmp_result;
-            }
-        }
-    }
-
-    if (update_id != NULL) {
-        crm_trace("Finished handling update for <%s " PCMK_XA_ID "='%s'>",
-                  update->name, update_id);
-    } else {
-        crm_trace("Finished handling update for <%s>", update->name);
-    }
-
-    return result;
-}
-
 static int
 add_cib_object(xmlNode * parent, xmlNode * new_obj)
 {
     const char *object_name = NULL;
     const char *object_id = NULL;
 
     if ((parent == NULL) || (new_obj == NULL)) {
         return -EINVAL;
     }
 
     object_name = (const char *) new_obj->name;
     if (object_name == NULL) {
         return -EINVAL;
     }
 
     object_id = pcmk__xe_id(new_obj);
     if (pcmk__xe_first_child(parent, object_name,
                              ((object_id != NULL)? PCMK_XA_ID : NULL),
                              object_id)) {
         return -EEXIST;
     }
 
     if (object_id != NULL) {
         crm_trace("Processing creation of <%s " PCMK_XA_ID "='%s'>",
                   object_name, object_id);
     } else {
         crm_trace("Processing creation of <%s>", object_name);
     }
-    return update_cib_object(parent, new_obj);
+
+    /* @COMPAT PCMK__XA_REPLACE is deprecated since 2.1.6. Due to a legacy use
+     * case, PCMK__XA_REPLACE has special meaning and should not be included in
+     * the newly created object until we can break behavioral backward
+     * compatibility.
+     *
+     * At a compatibility break, drop this and drop the definition of
+     * PCMK__XA_REPLACE. Treat it like any other attribute.
+     */
+    pcmk__xml_tree_foreach(new_obj, pcmk__xe_remove_attr_cb,
+                           (void *) PCMK__XA_REPLACE);
+
+    pcmk__xml_copy(parent, new_obj);
+    return pcmk_ok;
 }
 
 static bool
 update_results(xmlNode *failed, xmlNode *target, const char *operation,
                int return_code)
 {
     xmlNode *xml_node = NULL;
     bool was_error = false;
     const char *error_msg = NULL;
 
     if (return_code != pcmk_ok) {
         error_msg = pcmk_strerror(return_code);
 
         was_error = true;
         xml_node = pcmk__xe_create(failed, PCMK__XE_FAILED_UPDATE);
         pcmk__xml_copy(xml_node, target);
 
         crm_xml_add(xml_node, PCMK_XA_ID, pcmk__xe_id(target));
         crm_xml_add(xml_node, PCMK_XA_OBJECT_TYPE, (const char *) target->name);
         crm_xml_add(xml_node, PCMK_XA_OPERATION, operation);
         crm_xml_add(xml_node, PCMK_XA_REASON, error_msg);
 
         crm_warn("Action %s failed: %s (cde=%d)",
                  operation, error_msg, return_code);
     }
 
     return was_error;
 }
 
 int
 cib_process_create(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                    xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     xmlNode *failed = NULL;
     int result = pcmk_ok;
     xmlNode *update_section = NULL;
 
     crm_trace("Processing %s for %s section",
               op, pcmk__s(section, "unspecified"));
     if (pcmk__str_eq(PCMK__XE_ALL, section, pcmk__str_casei)) {
         section = NULL;
 
     } else if (pcmk__str_eq(section, PCMK_XE_CIB, pcmk__str_casei)) {
         section = NULL;
 
     } else if (pcmk__xe_is(input, PCMK_XE_CIB)) {
         section = NULL;
     }
 
     CRM_CHECK(strcmp(op, PCMK__CIB_REQUEST_CREATE) == 0, return -EINVAL);
 
     if (input == NULL) {
         crm_err("Cannot perform modification with no data");
         return -EINVAL;
     }
 
     if (section == NULL) {
         return cib_process_modify(op, options, section, req, input, existing_cib, result_cib,
                                   answer);
     }
 
+    // @COMPAT Deprecated since 2.1.8
     failed = pcmk__xe_create(NULL, PCMK__XE_FAILED);
 
     update_section = pcmk_find_cib_element(*result_cib, section);
     if (pcmk__xe_is(input, section)) {
         xmlNode *a_child = NULL;
 
         for (a_child = pcmk__xml_first_child(input); a_child != NULL;
              a_child = pcmk__xml_next(a_child)) {
             result = add_cib_object(update_section, a_child);
             if (update_results(failed, a_child, op, result)) {
                 break;
             }
         }
 
     } else {
         result = add_cib_object(update_section, input);
         update_results(failed, input, op, result);
     }
 
     if ((result == pcmk_ok) && (failed->children != NULL)) {
         result = -EINVAL;
     }
 
     if (result != pcmk_ok) {
         crm_log_xml_err(failed, "CIB Update failures");
         *answer = failed;
 
     } else {
         free_xml(failed);
     }
 
     return result;
 }
 
 int
 cib_process_diff(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
 {
     const char *originator = NULL;
 
     if (req != NULL) {
         originator = crm_element_value(req, PCMK__XA_SRC);
     }
 
     crm_trace("Processing \"%s\" event from %s%s",
               op, originator,
               (pcmk_is_set(options, cib_force_diff)? " (global update)" : ""));
 
     if (*result_cib != existing_cib) {
         free_xml(*result_cib);
     }
     *result_cib = pcmk__xml_copy(NULL, existing_cib);
 
     return xml_apply_patchset(*result_cib, input, TRUE);
 }
 
 // @COMPAT: v1-only
 bool
 cib__config_changed_v1(xmlNode *last, xmlNode *next, xmlNode **diff)
 {
     int lpc = 0, max = 0;
     bool config_changes = false;
     xmlXPathObject *xpathObj = NULL;
     int format = 1;
 
     CRM_ASSERT(diff != NULL);
 
     if (*diff == NULL && last != NULL && next != NULL) {
         *diff = pcmk__diff_v1_xml_object(last, next, false);
     }
 
     if (*diff == NULL) {
         goto done;
     }
 
     crm_element_value_int(*diff, PCMK_XA_FORMAT, &format);
     CRM_LOG_ASSERT(format == 1);
 
     xpathObj = xpath_search(*diff, "//" PCMK_XE_CONFIGURATION);
     if (numXpathResults(xpathObj) > 0) {
         config_changes = true;
         goto done;
     }
     freeXpathObject(xpathObj);
 
     /*
      * Do not check PCMK__XE_DIFF_ADDED "//" PCMK_XE_CIB
      * This always contains every field and would produce a false positive
      * every time if the checked value existed
      */
     xpathObj = xpath_search(*diff, "//" PCMK__XE_DIFF_REMOVED "//" PCMK_XE_CIB);
     max = numXpathResults(xpathObj);
 
     for (lpc = 0; lpc < max; lpc++) {
         xmlNode *top = getXpathResult(xpathObj, lpc);
 
         if (crm_element_value(top, PCMK_XA_EPOCH) != NULL) {
             config_changes = true;
             goto done;
         }
         if (crm_element_value(top, PCMK_XA_ADMIN_EPOCH) != NULL) {
             config_changes = true;
             goto done;
         }
 
         if (crm_element_value(top, PCMK_XA_VALIDATE_WITH) != NULL) {
             config_changes = true;
             goto done;
         }
         if (crm_element_value(top, PCMK_XA_CRM_FEATURE_SET) != NULL) {
             config_changes = true;
             goto done;
         }
         if (crm_element_value(top, PCMK_XA_REMOTE_CLEAR_PORT) != NULL) {
             config_changes = true;
             goto done;
         }
         if (crm_element_value(top, PCMK_XA_REMOTE_TLS_PORT) != NULL) {
             config_changes = true;
             goto done;
         }
     }
 
   done:
     freeXpathObject(xpathObj);
     return config_changes;
 }
 
 int
 cib_process_xpath(const char *op, int options, const char *section,
                   const xmlNode *req, xmlNode *input, xmlNode *existing_cib,
                   xmlNode **result_cib, xmlNode **answer)
 {
     int lpc = 0;
     int max = 0;
     int rc = pcmk_ok;
     bool is_query = pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none);
 
     xmlXPathObjectPtr xpathObj = NULL;
 
     crm_trace("Processing \"%s\" event", op);
 
     if (is_query) {
         xpathObj = xpath_search(existing_cib, section);
     } else {
         xpathObj = xpath_search(*result_cib, section);
     }
 
     max = numXpathResults(xpathObj);
 
     if ((max < 1)
         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
         crm_debug("%s was already removed", section);
 
     } else if (max < 1) {
         crm_debug("%s: %s does not exist", op, section);
         rc = -ENXIO;
 
     } else if (is_query) {
         if (max > 1) {
             *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
         }
     }
 
     if (pcmk_is_set(options, cib_multiple)
         && pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
         dedupXpathResults(xpathObj);
     }
 
     for (lpc = 0; lpc < max; lpc++) {
         xmlChar *path = NULL;
         xmlNode *match = getXpathResult(xpathObj, lpc);
 
         if (match == NULL) {
             continue;
         }
 
         path = xmlGetNodePath(match);
         crm_debug("Processing %s op for %s with %s", op, section, path);
         free(path);
 
         if (pcmk__str_eq(op, PCMK__CIB_REQUEST_DELETE, pcmk__str_none)) {
             if (match == *result_cib) {
                 /* Attempting to delete the whole "/cib" */
                 crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
                 rc = -EINVAL;
                 break;
             }
 
             free_xml(match);
             if ((options & cib_multiple) == 0) {
                 break;
             }
 
         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_MODIFY, pcmk__str_none)) {
             if (pcmk__xe_update_match(match, input) != pcmk_rc_ok) {
                 rc = -ENXIO;
             } else if ((options & cib_multiple) == 0) {
                 break;
             }
 
         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_CREATE, pcmk__str_none)) {
             pcmk__xml_copy(match, input);
             break;
 
         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
 
             if (options & cib_no_children) {
                 xmlNode *shallow = pcmk__xe_create(*answer,
                                                    (const char *) match->name);
 
                 copy_in_properties(shallow, match);
 
                 if (*answer == NULL) {
                     *answer = shallow;
                 }
 
             } else if (options & cib_xpath_address) {
                 char *path = NULL;
                 xmlNode *parent = match;
 
                 while (parent && parent->type == XML_ELEMENT_NODE) {
                     const char *id = crm_element_value(parent, PCMK_XA_ID);
                     char *new_path = NULL;
 
                     if (id) {
                         new_path = crm_strdup_printf("/%s[@" PCMK_XA_ID "='%s']"
                                                      "%s",
                                                      parent->name, id,
                                                      pcmk__s(path, ""));
                     } else {
                         new_path = crm_strdup_printf("/%s%s", parent->name,
                                                      pcmk__s(path, ""));
                     }
                     free(path);
                     path = new_path;
                     parent = parent->parent;
                 }
                 crm_trace("Got: %s", path);
 
                 if (*answer == NULL) {
                     *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY);
                 }
                 parent = pcmk__xe_create(*answer, PCMK__XE_XPATH_QUERY_PATH);
                 crm_xml_add(parent, PCMK_XA_ID, path);
                 free(path);
 
             } else if (*answer) {
                 pcmk__xml_copy(*answer, match);
 
             } else {
                 *answer = match;
             }
 
         } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_REPLACE,
                                 pcmk__str_none)) {
             xmlNode *parent = match->parent;
 
             free_xml(match);
             pcmk__xml_copy(parent, input);
 
             if ((options & cib_multiple) == 0) {
                 break;
             }
         }
     }
 
     freeXpathObject(xpathObj);
     return rc;
 }
diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h
index 4b3f984177..daf48e18d3 100644
--- a/lib/common/crmcommon_private.h
+++ b/lib/common/crmcommon_private.h
@@ -1,422 +1,418 @@
 /*
  * Copyright 2018-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 CRMCOMMON_PRIVATE__H
 #  define CRMCOMMON_PRIVATE__H
 
 /* This header is for the sole use of libcrmcommon, so that functions can be
  * declared with G_GNUC_INTERNAL for efficiency.
  */
 
 #include <stdint.h>         // uint8_t, uint32_t
 #include <stdbool.h>        // bool
 #include <sys/types.h>      // size_t
 #include <glib.h>           // gchar, GList
 #include <libxml/tree.h>    // xmlNode, xmlAttr
 #include <qb/qbipcc.h>      // struct qb_ipc_response_header
 
 // Decent chunk size for processing large amounts of data
 #define PCMK__BUFFER_SIZE 4096
 
 #if defined(PCMK__UNIT_TESTING)
 #undef G_GNUC_INTERNAL
 #define G_GNUC_INTERNAL
 #endif
 
 /* When deleting portions of an XML tree, we keep a record so we can know later
  * (e.g. when checking differences) that something was deleted.
  */
 typedef struct pcmk__deleted_xml_s {
     gchar *path;
     int position;
 } pcmk__deleted_xml_t;
 
 typedef struct xml_node_private_s {
         uint32_t check;
         uint32_t flags;
 } xml_node_private_t;
 
 typedef struct xml_doc_private_s {
         uint32_t check;
         uint32_t flags;
         char *user;
         GList *acls;
         GList *deleted_objs; // List of pcmk__deleted_xml_t
 } xml_doc_private_t;
 
 // XML entity references
 
 #define PCMK__XML_ENTITY_AMP    "&amp;"
 #define PCMK__XML_ENTITY_GT     "&gt;"
 #define PCMK__XML_ENTITY_LT     "&lt;"
 #define PCMK__XML_ENTITY_QUOT   "&quot;"
 
 //! libxml2 supports only XML version 1.0, at least as of libxml2-2.12.5
 #define PCMK__XML_VERSION ((pcmkXmlStr) "1.0")
 
 #define pcmk__set_xml_flags(xml_priv, flags_to_set) do {                    \
         (xml_priv)->flags = pcmk__set_flags_as(__func__, __LINE__,          \
             LOG_NEVER, "XML", "XML node", (xml_priv)->flags,                \
             (flags_to_set), #flags_to_set);                                 \
     } while (0)
 
 #define pcmk__clear_xml_flags(xml_priv, flags_to_clear) do {                \
         (xml_priv)->flags = pcmk__clear_flags_as(__func__, __LINE__,        \
             LOG_NEVER, "XML", "XML node", (xml_priv)->flags,                \
             (flags_to_clear), #flags_to_clear);                             \
     } while (0)
 
-G_GNUC_INTERNAL
-bool pcmk__xml_tree_foreach(xmlNode *xml, bool (*fn)(xmlNode *, void *),
-                            void *user_data);
-
 G_GNUC_INTERNAL
 bool pcmk__tracking_xml_changes(xmlNode *xml, bool lazy);
 
 G_GNUC_INTERNAL
-void pcmk__mark_xml_created(xmlNode *xml);
+void pcmk__xml_mark_created(xmlNode *xml);
 
 G_GNUC_INTERNAL
 int pcmk__xml_position(const xmlNode *xml,
                        enum xml_private_flags ignore_if_set);
 
 G_GNUC_INTERNAL
 xmlNode *pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle,
                          bool exact);
 
 G_GNUC_INTERNAL
 void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
                       bool as_diff);
 
 G_GNUC_INTERNAL
 xmlNode *pcmk__xc_match(const xmlNode *root, const xmlNode *search_comment,
                         bool exact);
 
 G_GNUC_INTERNAL
 void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update);
 
 G_GNUC_INTERNAL
 void pcmk__free_acls(GList *acls);
 
 G_GNUC_INTERNAL
 void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user);
 
 G_GNUC_INTERNAL
 bool pcmk__is_user_in_group(const char *user, const char *group);
 
 G_GNUC_INTERNAL
 void pcmk__apply_acl(xmlNode *xml);
 
 G_GNUC_INTERNAL
 void pcmk__apply_creation_acl(xmlNode *xml, bool check_top);
 
 G_GNUC_INTERNAL
 void pcmk__mark_xml_attr_dirty(xmlAttr *a);
 
 G_GNUC_INTERNAL
 bool pcmk__xa_filterable(const char *name);
 
 G_GNUC_INTERNAL
 void pcmk__log_xmllib_err(void *ctx, const char *fmt, ...)
 G_GNUC_PRINTF(2, 3);
 
 G_GNUC_INTERNAL
 void pcmk__mark_xml_node_dirty(xmlNode *xml);
 
 G_GNUC_INTERNAL
 bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data);
 
 G_GNUC_INTERNAL
 void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer);
 
 /*
  * Date/times
  */
 
 // For use with pcmk__add_time_from_xml()
 enum pcmk__time_component {
     pcmk__time_unknown,
     pcmk__time_years,
     pcmk__time_months,
     pcmk__time_weeks,
     pcmk__time_days,
     pcmk__time_hours,
     pcmk__time_minutes,
     pcmk__time_seconds,
 };
 
 G_GNUC_INTERNAL
 const char *pcmk__time_component_attr(enum pcmk__time_component component);
 
 G_GNUC_INTERNAL
 int pcmk__add_time_from_xml(crm_time_t *t, enum pcmk__time_component component,
                             const xmlNode *xml);
 
 G_GNUC_INTERNAL
 void pcmk__set_time_if_earlier(crm_time_t *target, const crm_time_t *source);
 
 
 /*
  * IPC
  */
 
 #define PCMK__IPC_VERSION 1
 
 #define PCMK__CONTROLD_API_MAJOR "1"
 #define PCMK__CONTROLD_API_MINOR "0"
 
 // IPC behavior that varies by daemon
 typedef struct pcmk__ipc_methods_s {
     /*!
      * \internal
      * \brief Allocate any private data needed by daemon IPC
      *
      * \param[in,out] api  IPC API connection
      *
      * \return Standard Pacemaker return code
      */
     int (*new_data)(pcmk_ipc_api_t *api);
 
     /*!
      * \internal
      * \brief Free any private data used by daemon IPC
      *
      * \param[in,out] api_data  Data allocated by new_data() method
      */
     void (*free_data)(void *api_data);
 
     /*!
      * \internal
      * \brief Perform daemon-specific handling after successful connection
      *
      * Some daemons require clients to register before sending any other
      * commands. The controller requires a CRM_OP_HELLO (with no reply), and
      * the CIB manager, executor, and fencer require a CRM_OP_REGISTER (with a
      * reply). Ideally this would be consistent across all daemons, but for now
      * this allows each to do its own authorization.
      *
      * \param[in,out] api  IPC API connection
      *
      * \return Standard Pacemaker return code
      */
     int (*post_connect)(pcmk_ipc_api_t *api);
 
     /*!
      * \internal
      * \brief Check whether an IPC request results in a reply
      *
      * \param[in,out] api      IPC API connection
      * \param[in]     request  IPC request XML
      *
      * \return true if request would result in an IPC reply, false otherwise
      */
     bool (*reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request);
 
     /*!
      * \internal
      * \brief Perform daemon-specific handling of an IPC message
      *
      * \param[in,out] api  IPC API connection
      * \param[in,out] msg  Message read from IPC connection
      *
      * \return true if more IPC reply messages should be expected
      */
     bool (*dispatch)(pcmk_ipc_api_t *api, xmlNode *msg);
 
     /*!
      * \internal
      * \brief Perform daemon-specific handling of an IPC disconnect
      *
      * \param[in,out] api  IPC API connection
      */
     void (*post_disconnect)(pcmk_ipc_api_t *api);
 } pcmk__ipc_methods_t;
 
 // Implementation of pcmk_ipc_api_t
 struct pcmk_ipc_api_s {
     enum pcmk_ipc_server server;          // Daemon this IPC API instance is for
     enum pcmk_ipc_dispatch dispatch_type; // How replies should be dispatched
     size_t ipc_size_max;                  // maximum IPC buffer size
     crm_ipc_t *ipc;                       // IPC connection
     mainloop_io_t *mainloop_io;     // If using mainloop, I/O source for IPC
     bool free_on_disconnect;        // Whether disconnect should free object
     pcmk_ipc_callback_t cb;         // Caller-registered callback (if any)
     void *user_data;                // Caller-registered data (if any)
     void *api_data;                 // For daemon-specific use
     pcmk__ipc_methods_t *cmds;      // Behavior that varies by daemon
 };
 
 typedef struct pcmk__ipc_header_s {
     struct qb_ipc_response_header qb;
     uint32_t size_uncompressed;
     uint32_t size_compressed;
     uint32_t flags;
     uint8_t version;
 } pcmk__ipc_header_t;
 
 G_GNUC_INTERNAL
 int pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request);
 
 G_GNUC_INTERNAL
 void pcmk__call_ipc_callback(pcmk_ipc_api_t *api,
                              enum pcmk_ipc_event event_type,
                              crm_exit_t status, void *event_data);
 
 G_GNUC_INTERNAL
 unsigned int pcmk__ipc_buffer_size(unsigned int max);
 
 G_GNUC_INTERNAL
 bool pcmk__valid_ipc_header(const pcmk__ipc_header_t *header);
 
 G_GNUC_INTERNAL
 pcmk__ipc_methods_t *pcmk__attrd_api_methods(void);
 
 G_GNUC_INTERNAL
 pcmk__ipc_methods_t *pcmk__controld_api_methods(void);
 
 G_GNUC_INTERNAL
 pcmk__ipc_methods_t *pcmk__pacemakerd_api_methods(void);
 
 G_GNUC_INTERNAL
 pcmk__ipc_methods_t *pcmk__schedulerd_api_methods(void);
 
 
 /*
  * Logging
  */
 
 //! XML is newly created
 #define PCMK__XML_PREFIX_CREATED "++"
 
 //! XML has been deleted
 #define PCMK__XML_PREFIX_DELETED "--"
 
 //! XML has been modified
 #define PCMK__XML_PREFIX_MODIFIED "+ "
 
 //! XML has been moved
 #define PCMK__XML_PREFIX_MOVED "+~"
 
 /*
  * Output
  */
 G_GNUC_INTERNAL
 int pcmk__bare_output_new(pcmk__output_t **out, const char *fmt_name,
                           const char *filename, char **argv);
 
 G_GNUC_INTERNAL
 void pcmk__register_option_messages(pcmk__output_t *out);
 
 G_GNUC_INTERNAL
 void pcmk__register_patchset_messages(pcmk__output_t *out);
 
 G_GNUC_INTERNAL
 bool pcmk__output_text_get_fancy(pcmk__output_t *out);
 
 /*
  * Rules
  */
 
 // How node attribute values may be compared in rules
 enum pcmk__comparison {
     pcmk__comparison_unknown,
     pcmk__comparison_defined,
     pcmk__comparison_undefined,
     pcmk__comparison_eq,
     pcmk__comparison_ne,
     pcmk__comparison_lt,
     pcmk__comparison_lte,
     pcmk__comparison_gt,
     pcmk__comparison_gte,
 };
 
 // How node attribute values may be parsed in rules
 enum pcmk__type {
     pcmk__type_unknown,
     pcmk__type_string,
     pcmk__type_integer,
     pcmk__type_number,
     pcmk__type_version,
 };
 
 // Where to obtain reference value for a node attribute comparison
 enum pcmk__reference_source {
     pcmk__source_unknown,
     pcmk__source_literal,
     pcmk__source_instance_attrs,
     pcmk__source_meta_attrs,
 };
 
 G_GNUC_INTERNAL
 enum pcmk__comparison pcmk__parse_comparison(const char *op);
 
 G_GNUC_INTERNAL
 enum pcmk__type pcmk__parse_type(const char *type, enum pcmk__comparison op,
                                  const char *value1, const char *value2);
 
 G_GNUC_INTERNAL
 enum pcmk__reference_source pcmk__parse_source(const char *source);
 
 G_GNUC_INTERNAL
 int pcmk__cmp_by_type(const char *value1, const char *value2,
                       enum pcmk__type type);
 
 G_GNUC_INTERNAL
 int pcmk__unpack_duration(const xmlNode *duration, const crm_time_t *start,
                           crm_time_t **end);
 
 G_GNUC_INTERNAL
 int pcmk__evaluate_date_spec(const xmlNode *date_spec, const crm_time_t *now);
 
 G_GNUC_INTERNAL
 int pcmk__evaluate_attr_expression(const xmlNode *expression,
                                    const pcmk_rule_input_t *rule_input);
 
 G_GNUC_INTERNAL
 int pcmk__evaluate_rsc_expression(const xmlNode *expr,
                                   const pcmk_rule_input_t *rule_input);
 
 G_GNUC_INTERNAL
 int pcmk__evaluate_op_expression(const xmlNode *expr,
                                  const pcmk_rule_input_t *rule_input);
 
 
 /*
  * Utils
  */
 #define PCMK__PW_BUFFER_LEN 500
 
 
 /*
  * Schemas
  */
 typedef struct {
     unsigned char v[2];
 } pcmk__schema_version_t;
 
 enum pcmk__schema_validator {
     pcmk__schema_validator_none,
     pcmk__schema_validator_rng
 };
 
 typedef struct {
     char *name;
     char *transform;
     void *cache;
     enum pcmk__schema_validator validator;
     pcmk__schema_version_t version;
     char *transform_enter;
     bool transform_onleave;
 } pcmk__schema_t;
 
 G_GNUC_INTERNAL
 int pcmk__find_x_0_schema_index(GList *schemas);
 
 
 #endif  // CRMCOMMON_PRIVATE__H
diff --git a/lib/common/patchset.c b/lib/common/patchset.c
index f385a365f5..7ddae4c474 100644
--- a/lib/common/patchset.c
+++ b/lib/common/patchset.c
@@ -1,1595 +1,1595 @@
 /*
  * Copyright 2004-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <time.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <bzlib.h>
 
 #include <libxml/tree.h>
 
 #include <crm/crm.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>  // CRM_XML_LOG_BASE, etc.
 #include "crmcommon_private.h"
 
 /* Add changes for specified XML to patchset.
  * For patchset format, refer to diff schema.
  */
 static void
 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
 {
     xmlNode *cIter = NULL;
     xmlAttr *pIter = NULL;
     xmlNode *change = NULL;
     xml_node_private_t *nodepriv = xml->_private;
     const char *value = NULL;
 
     if (nodepriv == NULL) {
         /* Elements that shouldn't occur in a CIB don't have _private set. They
          * should be stripped out, ignored, or have an error thrown by any code
          * that processes their parent, so we ignore any changes to them.
          */
         return;
     }
 
     // If this XML node is new, just report that
     if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
         GString *xpath = pcmk__element_xpath(xml->parent);
 
         if (xpath != NULL) {
             int position = pcmk__xml_position(xml, pcmk__xf_deleted);
 
             change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
 
             crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_CREATE);
             crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
             crm_xml_add_int(change, PCMK_XE_POSITION, position);
             pcmk__xml_copy(change, xml);
             g_string_free(xpath, TRUE);
         }
 
         return;
     }
 
     // Check each of the XML node's attributes for changes
     for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
          pIter = pIter->next) {
         xmlNode *attr = NULL;
 
         nodepriv = pIter->_private;
         if (!pcmk_any_flags_set(nodepriv->flags, pcmk__xf_deleted|pcmk__xf_dirty)) {
             continue;
         }
 
         if (change == NULL) {
             GString *xpath = pcmk__element_xpath(xml);
 
             if (xpath != NULL) {
                 change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
 
                 crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MODIFY);
                 crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
 
                 change = pcmk__xe_create(change, PCMK_XE_CHANGE_LIST);
                 g_string_free(xpath, TRUE);
             }
         }
 
         attr = pcmk__xe_create(change, PCMK_XE_CHANGE_ATTR);
 
         crm_xml_add(attr, PCMK_XA_NAME, (const char *) pIter->name);
         if (nodepriv->flags & pcmk__xf_deleted) {
             crm_xml_add(attr, PCMK_XA_OPERATION, "unset");
 
         } else {
             crm_xml_add(attr, PCMK_XA_OPERATION, "set");
 
             value = pcmk__xml_attr_value(pIter);
             crm_xml_add(attr, PCMK_XA_VALUE, value);
         }
     }
 
     if (change) {
         xmlNode *result = NULL;
 
         change = pcmk__xe_create(change->parent, PCMK_XE_CHANGE_RESULT);
         result = pcmk__xe_create(change, (const char *)xml->name);
 
         for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
              pIter = pIter->next) {
             nodepriv = pIter->_private;
             if (!pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
                 value = crm_element_value(xml, (const char *) pIter->name);
                 crm_xml_add(result, (const char *)pIter->name, value);
             }
         }
     }
 
     // Now recursively do the same for each child node of this node
     for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
          cIter = pcmk__xml_next(cIter)) {
         add_xml_changes_to_patchset(cIter, patchset);
     }
 
     nodepriv = xml->_private;
     if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
         GString *xpath = pcmk__element_xpath(xml);
 
         crm_trace("%s.%s moved to position %d",
                   xml->name, pcmk__xe_id(xml),
                   pcmk__xml_position(xml, pcmk__xf_skip));
 
         if (xpath != NULL) {
             change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
 
             crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_MOVE);
             crm_xml_add(change, PCMK_XA_PATH, (const char *) xpath->str);
             crm_xml_add_int(change, PCMK_XE_POSITION,
                             pcmk__xml_position(xml, pcmk__xf_deleted));
             g_string_free(xpath, TRUE);
         }
     }
 }
 
 static bool
 is_config_change(xmlNode *xml)
 {
     GList *gIter = NULL;
     xml_node_private_t *nodepriv = NULL;
     xml_doc_private_t *docpriv;
     xmlNode *config = pcmk__xe_first_child(xml, PCMK_XE_CONFIGURATION, NULL,
                                            NULL);
 
     if (config) {
         nodepriv = config->_private;
     }
     if ((nodepriv != NULL) && pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
         return TRUE;
     }
 
     if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
         docpriv = xml->doc->_private;
         for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
             pcmk__deleted_xml_t *deleted_obj = gIter->data;
 
             if (strstr(deleted_obj->path,
                        "/" PCMK_XE_CIB "/" PCMK_XE_CONFIGURATION) != NULL) {
                 return TRUE;
             }
         }
     }
     return FALSE;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static void
 xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
                    gboolean changed)
 {
     int lpc = 0;
     xmlNode *cib = NULL;
     xmlNode *diff_child = NULL;
 
     const char *tag = NULL;
 
     const char *vfields[] = {
         PCMK_XA_ADMIN_EPOCH,
         PCMK_XA_EPOCH,
         PCMK_XA_NUM_UPDATES,
     };
 
     if (local_diff == NULL) {
         crm_trace("Nothing to do");
         return;
     }
 
     tag = PCMK__XE_DIFF_REMOVED;
     diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL);
     if (diff_child == NULL) {
         diff_child = pcmk__xe_create(local_diff, tag);
     }
 
     tag = PCMK_XE_CIB;
     cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL);
     if (cib == NULL) {
         cib = pcmk__xe_create(diff_child, tag);
     }
 
     for (lpc = 0; (last != NULL) && (lpc < PCMK__NELEM(vfields)); lpc++) {
         const char *value = crm_element_value(last, vfields[lpc]);
 
         crm_xml_add(diff_child, vfields[lpc], value);
         if (changed || lpc == 2) {
             crm_xml_add(cib, vfields[lpc], value);
         }
     }
 
     tag = PCMK__XE_DIFF_ADDED;
     diff_child = pcmk__xe_first_child(local_diff, tag, NULL, NULL);
     if (diff_child == NULL) {
         diff_child = pcmk__xe_create(local_diff, tag);
     }
 
     tag = PCMK_XE_CIB;
     cib = pcmk__xe_first_child(diff_child, tag, NULL, NULL);
     if (cib == NULL) {
         cib = pcmk__xe_create(diff_child, tag);
     }
 
     for (lpc = 0; next && lpc < PCMK__NELEM(vfields); lpc++) {
         const char *value = crm_element_value(next, vfields[lpc]);
 
         crm_xml_add(diff_child, vfields[lpc], value);
     }
 
     for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
         
         const char *p_value = pcmk__xml_attr_value(a);
 
         xmlSetProp(cib, a->name, (pcmkXmlStr) p_value);
     }
 
     crm_log_xml_explicit(local_diff, "Repaired-diff");
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static xmlNode *
 xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config,
                        bool suppress)
 {
     xmlNode *patchset = pcmk__diff_v1_xml_object(source, target, suppress);
 
     if (patchset) {
         CRM_LOG_ASSERT(xml_document_dirty(target));
         xml_repair_v1_diff(source, target, patchset, config);
         crm_xml_add(patchset, PCMK_XA_FORMAT, "1");
     }
     return patchset;
 }
 
 static xmlNode *
 xml_create_patchset_v2(xmlNode *source, xmlNode *target)
 {
     int lpc = 0;
     GList *gIter = NULL;
     xml_doc_private_t *docpriv;
 
     xmlNode *v = NULL;
     xmlNode *version = NULL;
     xmlNode *patchset = NULL;
     const char *vfields[] = {
         PCMK_XA_ADMIN_EPOCH,
         PCMK_XA_EPOCH,
         PCMK_XA_NUM_UPDATES,
     };
 
     CRM_ASSERT(target);
     if (!xml_document_dirty(target)) {
         return NULL;
     }
 
     CRM_ASSERT(target->doc);
     docpriv = target->doc->_private;
 
     patchset = pcmk__xe_create(NULL, PCMK_XE_DIFF);
     crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2);
 
     version = pcmk__xe_create(patchset, PCMK_XE_VERSION);
 
     v = pcmk__xe_create(version, PCMK_XE_SOURCE);
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         const char *value = crm_element_value(source, vfields[lpc]);
 
         if (value == NULL) {
             value = "1";
         }
         crm_xml_add(v, vfields[lpc], value);
     }
 
     v = pcmk__xe_create(version, PCMK_XE_TARGET);
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         const char *value = crm_element_value(target, vfields[lpc]);
 
         if (value == NULL) {
             value = "1";
         }
         crm_xml_add(v, vfields[lpc], value);
     }
 
     for (gIter = docpriv->deleted_objs; gIter; gIter = gIter->next) {
         pcmk__deleted_xml_t *deleted_obj = gIter->data;
         xmlNode *change = pcmk__xe_create(patchset, PCMK_XE_CHANGE);
 
         crm_xml_add(change, PCMK_XA_OPERATION, PCMK_VALUE_DELETE);
         crm_xml_add(change, PCMK_XA_PATH, deleted_obj->path);
         if (deleted_obj->position >= 0) {
             crm_xml_add_int(change, PCMK_XE_POSITION, deleted_obj->position);
         }
     }
 
     add_xml_changes_to_patchset(target, patchset);
     return patchset;
 }
 
 xmlNode *
 xml_create_patchset(int format, xmlNode *source, xmlNode *target,
                     bool *config_changed, bool manage_version)
 {
     int counter = 0;
     bool config = FALSE;
     xmlNode *patch = NULL;
     const char *version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET);
 
     xml_acl_disable(target);
     if (!xml_document_dirty(target)) {
         crm_trace("No change %d", format);
         return NULL; /* No change */
     }
 
     config = is_config_change(target);
     if (config_changed) {
         *config_changed = config;
     }
 
     if (manage_version && config) {
         crm_trace("Config changed %d", format);
         crm_xml_add(target, PCMK_XA_NUM_UPDATES, "0");
 
         crm_element_value_int(target, PCMK_XA_EPOCH, &counter);
         crm_xml_add_int(target, PCMK_XA_EPOCH, counter+1);
 
     } else if (manage_version) {
         crm_element_value_int(target, PCMK_XA_NUM_UPDATES, &counter);
         crm_trace("Status changed %d - %d %s", format, counter,
                   crm_element_value(source, PCMK_XA_NUM_UPDATES));
         crm_xml_add_int(target, PCMK_XA_NUM_UPDATES, (counter + 1));
     }
 
     if (format == 0) {
         if (compare_version("3.0.8", version) < 0) {
             format = 2;
         } else {
             format = 1;
         }
         crm_trace("Using patch format %d for version: %s", format, version);
     }
 
     switch (format) {
         case 1:
             // @COMPAT Remove when v1 patchsets are removed
             patch = xml_create_patchset_v1(source, target, config, FALSE);
             break;
         case 2:
             patch = xml_create_patchset_v2(source, target);
             break;
         default:
             crm_err("Unknown patch format: %d", format);
             return NULL;
     }
     return patch;
 }
 
 void
 patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target,
                         bool with_digest)
 {
     int format = 1;
     const char *version = NULL;
     char *digest = NULL;
 
     if ((patch == NULL) || (source == NULL) || (target == NULL)) {
         return;
     }
 
     /* We should always call xml_accept_changes() before calculating a digest.
      * Otherwise, with an on-tracking dirty target, we could get a wrong digest.
      */
     CRM_LOG_ASSERT(!xml_document_dirty(target));
 
     crm_element_value_int(patch, PCMK_XA_FORMAT, &format);
     if ((format > 1) && !with_digest) {
         return;
     }
 
     version = crm_element_value(source, PCMK_XA_CRM_FEATURE_SET);
     digest = calculate_xml_versioned_digest(target, FALSE, TRUE, version);
 
     crm_xml_add(patch, PCMK__XA_DIGEST, digest);
     free(digest);
 
     return;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static xmlNode *
 subtract_v1_xml_comment(xmlNode *parent, xmlNode *left, xmlNode *right,
                         gboolean *changed)
 {
     CRM_CHECK(left != NULL, return NULL);
     CRM_CHECK(left->type == XML_COMMENT_NODE, return NULL);
 
     if ((right == NULL) || !pcmk__str_eq((const char *)left->content,
                                          (const char *)right->content,
                                          pcmk__str_casei)) {
         xmlNode *deleted = NULL;
 
         deleted = pcmk__xml_copy(parent, left);
         *changed = TRUE;
 
         return deleted;
     }
 
     return NULL;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static xmlNode *
 subtract_v1_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
                        bool full, gboolean *changed, const char *marker)
 {
     gboolean dummy = FALSE;
     xmlNode *diff = NULL;
     xmlNode *right_child = NULL;
     xmlNode *left_child = NULL;
     xmlAttrPtr xIter = NULL;
 
     const char *id = NULL;
     const char *name = NULL;
     const char *value = NULL;
     const char *right_val = NULL;
 
     if (changed == NULL) {
         changed = &dummy;
     }
 
     if (left == NULL) {
         return NULL;
     }
 
     if (left->type == XML_COMMENT_NODE) {
         return subtract_v1_xml_comment(parent, left, right, changed);
     }
 
     id = pcmk__xe_id(left);
     name = (const char *) left->name;
     if (right == NULL) {
         xmlNode *deleted = NULL;
 
         crm_trace("Processing <%s " PCMK_XA_ID "=%s> (complete copy)",
                   name, id);
         deleted = pcmk__xml_copy(parent, left);
         crm_xml_add(deleted, PCMK__XA_CRM_DIFF_MARKER, marker);
 
         *changed = TRUE;
         return deleted;
     }
 
     CRM_CHECK(name != NULL, return NULL);
     CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL);
 
     // Check for PCMK__XA_CRM_DIFF_MARKER in a child
     value = crm_element_value(right, PCMK__XA_CRM_DIFF_MARKER);
     if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
         crm_trace("We are the root of the deletion: %s.id=%s", name, id);
         *changed = TRUE;
         return NULL;
     }
 
     // @TODO Avoiding creating the full hierarchy would save work here
     diff = pcmk__xe_create(parent, name);
 
     // Changes to child objects
     for (left_child = pcmk__xml_first_child(left); left_child != NULL;
          left_child = pcmk__xml_next(left_child)) {
         gboolean child_changed = FALSE;
 
         right_child = pcmk__xml_match(right, left_child, false);
         subtract_v1_xml_object(diff, left_child, right_child, full,
                                &child_changed, marker);
         if (child_changed) {
             *changed = TRUE;
         }
     }
 
     if (!*changed) {
         /* Nothing to do */
 
     } else if (full) {
         xmlAttrPtr pIter = NULL;
 
         for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
              pIter = pIter->next) {
             const char *p_name = (const char *)pIter->name;
             const char *p_value = pcmk__xml_attr_value(pIter);
 
             xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
         }
 
         // We have everything we need
         goto done;
     }
 
     // Changes to name/value pairs
     for (xIter = pcmk__xe_first_attr(left); xIter != NULL;
          xIter = xIter->next) {
         const char *prop_name = (const char *) xIter->name;
         xmlAttrPtr right_attr = NULL;
         xml_node_private_t *nodepriv = NULL;
 
         if (strcmp(prop_name, PCMK_XA_ID) == 0) {
             // id already obtained when present ~ this case, so just reuse
             xmlSetProp(diff, (pcmkXmlStr) PCMK_XA_ID, (pcmkXmlStr) id);
             continue;
         }
 
         if (pcmk__xa_filterable(prop_name)) {
             continue;
         }
 
         right_attr = xmlHasProp(right, (pcmkXmlStr) prop_name);
         if (right_attr) {
             nodepriv = right_attr->_private;
         }
 
         right_val = crm_element_value(right, prop_name);
         if ((right_val == NULL) || (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted))) {
             /* new */
             *changed = TRUE;
             if (full) {
                 xmlAttrPtr pIter = NULL;
 
                 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
                      pIter = pIter->next) {
                     const char *p_name = (const char *) pIter->name;
                     const char *p_value = pcmk__xml_attr_value(pIter);
 
                     xmlSetProp(diff, (pcmkXmlStr) p_name, (pcmkXmlStr) p_value);
                 }
                 break;
 
             } else {
                 const char *left_value = pcmk__xml_attr_value(xIter);
 
                 xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value);
                 crm_xml_add(diff, prop_name, left_value);
             }
 
         } else {
             /* Only now do we need the left value */
             const char *left_value = pcmk__xml_attr_value(xIter);
 
             if (strcmp(left_value, right_val) == 0) {
                 /* unchanged */
 
             } else {
                 *changed = TRUE;
                 if (full) {
                     xmlAttrPtr pIter = NULL;
 
                     crm_trace("Changes detected to %s in "
                               "<%s " PCMK_XA_ID "=%s>", prop_name, name, id);
                     for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
                          pIter = pIter->next) {
                         const char *p_name = (const char *) pIter->name;
                         const char *p_value = pcmk__xml_attr_value(pIter);
 
                         xmlSetProp(diff, (pcmkXmlStr) p_name,
                                    (pcmkXmlStr) p_value);
                     }
                     break;
 
                 } else {
                     crm_trace("Changes detected to %s (%s -> %s) in "
                               "<%s " PCMK_XA_ID "=%s>",
                               prop_name, left_value, right_val, name, id);
                     crm_xml_add(diff, prop_name, left_value);
                 }
             }
         }
     }
 
     if (!*changed) {
         free_xml(diff);
         return NULL;
 
     } else if (!full && (id != NULL)) {
         crm_xml_add(diff, PCMK_XA_ID, id);
     }
   done:
     return diff;
 }
 
 /* @COMPAT Remove when v1 patchsets are removed.
  *
  * Return true if attribute name is not \c PCMK_XML_ID.
  */
 static bool
 not_id(xmlAttrPtr attr, void *user_data)
 {
     return strcmp((const char *) attr->name, PCMK_XA_ID) != 0;
 }
 
 /* @COMPAT Remove when v1 patchsets are removed.
  *
  * Apply the removals section of a v1 patchset to an XML node.
  */
 static void
 process_v1_removals(xmlNode *target, xmlNode *patch)
 {
     xmlNode *patch_child = NULL;
     xmlNode *cIter = NULL;
 
     char *id = NULL;
     const char *value = NULL;
 
     if ((target == NULL) || (patch == NULL)) {
         return;
     }
 
     if (target->type == XML_COMMENT_NODE) {
         gboolean dummy;
 
         subtract_v1_xml_comment(target->parent, target, patch, &dummy);
     }
 
     CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return);
     CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
                            pcmk__str_none),
               return);
 
     // Check for PCMK__XA_CRM_DIFF_MARKER in a child
     id = crm_element_value_copy(target, PCMK_XA_ID);
     value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
     if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
         crm_trace("We are the root of the deletion: %s.id=%s",
                   target->name, id);
         free_xml(target);
         free(id);
         return;
     }
 
     // Removing then restoring id would change ordering of properties
     pcmk__xe_remove_matching_attrs(patch, not_id, NULL);
 
     // Changes to child objects
     cIter = pcmk__xml_first_child(target);
     while (cIter) {
         xmlNode *target_child = cIter;
 
         cIter = pcmk__xml_next(cIter);
         patch_child = pcmk__xml_match(patch, target_child, false);
         process_v1_removals(target_child, patch_child);
     }
     free(id);
 }
 
 /* @COMPAT Remove when v1 patchsets are removed.
  *
  * Apply the additions section of a v1 patchset to an XML node.
  */
 static void
 process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
 {
     xmlNode *patch_child = NULL;
     xmlNode *target_child = NULL;
     xmlAttrPtr xIter = NULL;
 
     const char *id = NULL;
     const char *name = NULL;
     const char *value = NULL;
 
     if (patch == NULL) {
         return;
     } else if ((parent == NULL) && (target == NULL)) {
         return;
     }
 
     // Check for PCMK__XA_CRM_DIFF_MARKER in a child
     name = (const char *) patch->name;
     value = crm_element_value(patch, PCMK__XA_CRM_DIFF_MARKER);
     if ((target == NULL) && (value != NULL)
         && (strcmp(value, "added:top") == 0)) {
         id = pcmk__xe_id(patch);
         crm_trace("We are the root of the addition: %s.id=%s", name, id);
         pcmk__xml_copy(parent, patch);
         return;
 
     } else if (target == NULL) {
         id = pcmk__xe_id(patch);
         crm_err("Could not locate: %s.id=%s", name, id);
         return;
     }
 
     if (target->type == XML_COMMENT_NODE) {
         pcmk__xc_update(parent, target, patch);
     }
 
     CRM_CHECK(pcmk__xe_is(target, name), return);
     CRM_CHECK(pcmk__str_eq(pcmk__xe_id(target), pcmk__xe_id(patch),
                            pcmk__str_none),
               return);
 
     for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
          xIter = xIter->next) {
         const char *p_name = (const char *) xIter->name;
         const char *p_value = pcmk__xml_attr_value(xIter);
 
         pcmk__xe_remove_attr(target, p_name);   // Preserve patch order
         crm_xml_add(target, p_name, p_value);
     }
 
     // Changes to child objects
     for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
          patch_child = pcmk__xml_next(patch_child)) {
 
         target_child = pcmk__xml_match(target, patch_child, false);
         process_v1_additions(target, target_child, patch_child);
     }
 }
 
 /*!
  * \internal
  * \brief Find additions or removals in a patch set
  *
  * \param[in]     patchset   XML of patch
  * \param[in]     format     Patch version
  * \param[in]     added      TRUE if looking for additions, FALSE if removals
  * \param[in,out] patch_node Will be set to node if found
  *
  * \return TRUE if format is valid, FALSE if invalid
  */
 static bool
 find_patch_xml_node(const xmlNode *patchset, int format, bool added,
                     xmlNode **patch_node)
 {
     xmlNode *cib_node;
     const char *label;
 
     switch (format) {
         case 1:
             // @COMPAT Remove when v1 patchsets are removed
             label = added? PCMK__XE_DIFF_ADDED : PCMK__XE_DIFF_REMOVED;
             *patch_node = pcmk__xe_first_child(patchset, label, NULL, NULL);
             cib_node = pcmk__xe_first_child(*patch_node, PCMK_XE_CIB, NULL,
                                             NULL);
             if (cib_node != NULL) {
                 *patch_node = cib_node;
             }
             break;
         case 2:
             label = added? PCMK_XE_TARGET : PCMK_XE_SOURCE;
             *patch_node = pcmk__xe_first_child(patchset, PCMK_XE_VERSION, NULL,
                                                NULL);
             *patch_node = pcmk__xe_first_child(*patch_node, label, NULL, NULL);
             break;
         default:
             crm_warn("Unknown patch format: %d", format);
             *patch_node = NULL;
             return FALSE;
     }
     return TRUE;
 }
 
 // Get CIB versions used for additions and deletions in a patchset
 bool
 xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
 {
     int lpc = 0;
     int format = 1;
     xmlNode *tmp = NULL;
 
     const char *vfields[] = {
         PCMK_XA_ADMIN_EPOCH,
         PCMK_XA_EPOCH,
         PCMK_XA_NUM_UPDATES,
     };
 
 
     crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
 
     /* Process removals */
     if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
         return -EINVAL;
     }
     if (tmp != NULL) {
         for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
             crm_element_value_int(tmp, vfields[lpc], &(del[lpc]));
             crm_trace("Got %d for del[%s]", del[lpc], vfields[lpc]);
         }
     }
 
     /* Process additions */
     if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
         return -EINVAL;
     }
     if (tmp != NULL) {
         for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
             crm_element_value_int(tmp, vfields[lpc], &(add[lpc]));
             crm_trace("Got %d for add[%s]", add[lpc], vfields[lpc]);
         }
     }
     return pcmk_ok;
 }
 
 /*!
  * \internal
  * \brief Check whether patchset can be applied to current CIB
  *
  * \param[in] xml       Root of current CIB
  * \param[in] patchset  Patchset to check
  *
  * \return Standard Pacemaker return code
  */
 static int
 xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
 {
     int lpc = 0;
     bool changed = FALSE;
 
     int this[] = { 0, 0, 0 };
     int add[] = { 0, 0, 0 };
     int del[] = { 0, 0, 0 };
 
     const char *vfields[] = {
         PCMK_XA_ADMIN_EPOCH,
         PCMK_XA_EPOCH,
         PCMK_XA_NUM_UPDATES,
     };
 
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         crm_element_value_int(xml, vfields[lpc], &(this[lpc]));
         crm_trace("Got %d for this[%s]", this[lpc], vfields[lpc]);
         if (this[lpc] < 0) {
             this[lpc] = 0;
         }
     }
 
     /* Set some defaults in case nothing is present */
     add[0] = this[0];
     add[1] = this[1];
     add[2] = this[2] + 1;
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         del[lpc] = this[lpc];
     }
 
     xml_patch_versions(patchset, add, del);
 
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         if (this[lpc] < del[lpc]) {
             crm_debug("Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
                       vfields[lpc], this[0], this[1], this[2],
                       del[0], del[1], del[2], add[0], add[1], add[2]);
             return pcmk_rc_diff_resync;
 
         } else if (this[lpc] > del[lpc]) {
             crm_info("Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
                      vfields[lpc], this[0], this[1], this[2],
                      del[0], del[1], del[2], add[0], add[1], add[2], patchset);
             crm_log_xml_info(patchset, "OldPatch");
             return pcmk_rc_old_data;
         }
     }
 
     for (lpc = 0; lpc < PCMK__NELEM(vfields); lpc++) {
         if (add[lpc] > del[lpc]) {
             changed = TRUE;
         }
     }
 
     if (!changed) {
         crm_notice("Versions did not change in patch %d.%d.%d",
                    add[0], add[1], add[2]);
         return pcmk_rc_old_data;
     }
 
     crm_debug("Can apply patch %d.%d.%d to %d.%d.%d",
               add[0], add[1], add[2], this[0], this[1], this[2]);
     return pcmk_rc_ok;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static void
 purge_v1_diff_markers(xmlNode *node)
 {
     xmlNode *child = NULL;
 
     CRM_CHECK(node != NULL, return);
 
     pcmk__xe_remove_attr(node, PCMK__XA_CRM_DIFF_MARKER);
     for (child = pcmk__xml_first_child(node); child != NULL;
          child = pcmk__xml_next(child)) {
         purge_v1_diff_markers(child);
     }
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 /*!
  * \internal
  * \brief Apply a version 1 patchset to an XML node
  *
  * \param[in,out] xml       XML to apply patchset to
  * \param[in]     patchset  Patchset to apply
  *
  * \return Standard Pacemaker return code
  */
 static int
 apply_v1_patchset(xmlNode *xml, const xmlNode *patchset)
 {
     int rc = pcmk_rc_ok;
     int root_nodes_seen = 0;
 
     xmlNode *child_diff = NULL;
     xmlNode *added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL,
                                           NULL);
     xmlNode *removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED,
                                             NULL, NULL);
     xmlNode *old = pcmk__xml_copy(NULL, xml);
 
     crm_trace("Subtraction Phase");
     for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
          child_diff = pcmk__xml_next(child_diff)) {
         CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
         if (root_nodes_seen == 0) {
             process_v1_removals(xml, child_diff);
         }
         root_nodes_seen++;
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(-) Diffs cannot contain more than one change set... saw %d",
                 root_nodes_seen);
         rc = ENOTUNIQ;
     }
 
     root_nodes_seen = 0;
     crm_trace("Addition Phase");
     if (rc == pcmk_rc_ok) {
         xmlNode *child_diff = NULL;
 
         for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
              child_diff = pcmk__xml_next(child_diff)) {
             CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
             if (root_nodes_seen == 0) {
                 process_v1_additions(NULL, xml, child_diff);
             }
             root_nodes_seen++;
         }
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(+) Diffs cannot contain more than one change set... saw %d",
                 root_nodes_seen);
         rc = ENOTUNIQ;
     }
 
     purge_v1_diff_markers(xml); // Purge prior to checking digest
 
     free_xml(old);
     return rc;
 }
 
 // Return first child matching element name and optionally id or position
 static xmlNode *
 first_matching_xml_child(const xmlNode *parent, const char *name,
                          const char *id, int position)
 {
     xmlNode *cIter = NULL;
 
     for (cIter = pcmk__xml_first_child(parent); cIter != NULL;
          cIter = pcmk__xml_next(cIter)) {
         if (strcmp((const char *) cIter->name, name) != 0) {
             continue;
         } else if (id) {
             const char *cid = pcmk__xe_id(cIter);
 
             if ((cid == NULL) || (strcmp(cid, id) != 0)) {
                 continue;
             }
         }
 
         // "position" makes sense only for XML comments for now
         if ((cIter->type == XML_COMMENT_NODE)
             && (position >= 0)
             && (pcmk__xml_position(cIter, pcmk__xf_skip) != position)) {
             continue;
         }
 
         return cIter;
     }
     return NULL;
 }
 
 /*!
  * \internal
  * \brief Simplified, more efficient alternative to get_xpath_object()
  *
  * \param[in] top              Root of XML to search
  * \param[in] key              Search xpath
  * \param[in] target_position  If deleting, where to delete
  *
  * \return XML child matching xpath if found, NULL otherwise
  *
  * \note This only works on simplified xpaths found in v2 patchset diffs,
  *       i.e. the only allowed search predicate is [@id='XXX'].
  */
 static xmlNode *
 search_v2_xpath(const xmlNode *top, const char *key, int target_position)
 {
     xmlNode *target = (xmlNode *) top->doc;
     const char *current = key;
     char *section;
     char *remainder;
     char *id;
     char *tag;
     char *path = NULL;
     int rc;
     size_t key_len;
 
     CRM_CHECK(key != NULL, return NULL);
     key_len = strlen(key);
 
     /* These are scanned from key after a slash, so they can't be bigger
      * than key_len - 1 characters plus a null terminator.
      */
 
     remainder = pcmk__assert_alloc(key_len, sizeof(char));
     section = pcmk__assert_alloc(key_len, sizeof(char));
     id = pcmk__assert_alloc(key_len, sizeof(char));
     tag = pcmk__assert_alloc(key_len, sizeof(char));
 
     do {
         // Look for /NEXT_COMPONENT/REMAINING_COMPONENTS
         rc = sscanf(current, "/%[^/]%s", section, remainder);
         if (rc > 0) {
             // Separate FIRST_COMPONENT into TAG[@id='ID']
             int f = sscanf(section, "%[^[][@" PCMK_XA_ID "='%[^']", tag, id);
             int current_position = -1;
 
             /* The target position is for the final component tag, so only use
              * it if there is nothing left to search after this component.
              */
             if ((rc == 1) && (target_position >= 0)) {
                 current_position = target_position;
             }
 
             switch (f) {
                 case 1:
                     // @COMPAT Remove when v1 patchsets are removed
                     target = first_matching_xml_child(target, tag, NULL,
                                                       current_position);
                     break;
                 case 2:
                     target = first_matching_xml_child(target, tag, id,
                                                       current_position);
                     break;
                 default:
                     // This should not be possible
                     target = NULL;
                     break;
             }
             current = remainder;
         }
 
     // Continue if something remains to search, and we've matched so far
     } while ((rc == 2) && target);
 
     if (target) {
         crm_trace("Found %s for %s",
                   (path = (char *) xmlGetNodePath(target)), key);
         free(path);
     } else {
         crm_debug("No match for %s", key);
     }
 
     free(remainder);
     free(section);
     free(tag);
     free(id);
     return target;
 }
 
 typedef struct xml_change_obj_s {
     const xmlNode *change;
     xmlNode *match;
 } xml_change_obj_t;
 
 static gint
 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
 {
     const xml_change_obj_t *change_obj_a = a;
     const xml_change_obj_t *change_obj_b = b;
     int position_a = -1;
     int position_b = -1;
 
     crm_element_value_int(change_obj_a->change, PCMK_XE_POSITION, &position_a);
     crm_element_value_int(change_obj_b->change, PCMK_XE_POSITION, &position_b);
 
     if (position_a < position_b) {
         return -1;
 
     } else if (position_a > position_b) {
         return 1;
     }
 
     return 0;
 }
 
 /*!
  * \internal
  * \brief Apply a version 2 patchset to an XML node
  *
  * \param[in,out] xml       XML to apply patchset to
  * \param[in]     patchset  Patchset to apply
  *
  * \return Standard Pacemaker return code
  */
 static int
 apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
 {
     int rc = pcmk_rc_ok;
     const xmlNode *change = NULL;
     GList *change_objs = NULL;
     GList *gIter = NULL;
 
     for (change = pcmk__xml_first_child(patchset); change != NULL;
          change = pcmk__xml_next(change)) {
         xmlNode *match = NULL;
         const char *op = crm_element_value(change, PCMK_XA_OPERATION);
         const char *xpath = crm_element_value(change, PCMK_XA_PATH);
         int position = -1;
 
         if (op == NULL) {
             continue;
         }
 
         crm_trace("Processing %s %s", change->name, op);
 
         /* PCMK_VALUE_DELETE changes for XML comments are generated with
          * PCMK_XE_POSITION
          */
         if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
             crm_element_value_int(change, PCMK_XE_POSITION, &position);
         }
         match = search_v2_xpath(xml, xpath, position);
         crm_trace("Performing %s on %s with %p", op, xpath, match);
 
         if ((match == NULL) && (strcmp(op, PCMK_VALUE_DELETE) == 0)) {
             crm_debug("No %s match for %s in %p", op, xpath, xml->doc);
             continue;
 
         } else if (match == NULL) {
             crm_err("No %s match for %s in %p", op, xpath, xml->doc);
             rc = pcmk_rc_diff_failed;
             continue;
 
         } else if (pcmk__str_any_of(op,
                                     PCMK_VALUE_CREATE, PCMK_VALUE_MOVE, NULL)) {
             // Delay the adding of a PCMK_VALUE_CREATE object
             xml_change_obj_t *change_obj =
                 pcmk__assert_alloc(1, sizeof(xml_change_obj_t));
 
             change_obj->change = change;
             change_obj->match = match;
 
             change_objs = g_list_append(change_objs, change_obj);
 
             if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
                 // Temporarily put the PCMK_VALUE_MOVE object after the last sibling
                 if ((match->parent != NULL) && (match->parent->last != NULL)) {
                     xmlAddNextSibling(match->parent->last, match);
                 }
             }
 
         } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
             free_xml(match);
 
         } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
             const xmlNode *child = pcmk__xe_first_child(change,
                                                         PCMK_XE_CHANGE_RESULT,
                                                         NULL, NULL);
             const xmlNode *attrs = pcmk__xml_first_child(child);
 
             if (attrs == NULL) {
                 rc = ENOMSG;
                 continue;
             }
             pcmk__xe_remove_matching_attrs(match, NULL, NULL); // Remove all
 
             for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
                  pIter = pIter->next) {
                 const char *name = (const char *) pIter->name;
                 const char *value = pcmk__xml_attr_value(pIter);
 
                 crm_xml_add(match, name, value);
             }
 
         } else {
             crm_err("Unknown operation: %s", op);
             rc = pcmk_rc_diff_failed;
         }
     }
 
     // Changes should be generated in the right order. Double checking.
     change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
 
     for (gIter = change_objs; gIter; gIter = gIter->next) {
         xml_change_obj_t *change_obj = gIter->data;
         xmlNode *match = change_obj->match;
         const char *op = NULL;
         const char *xpath = NULL;
 
         change = change_obj->change;
 
         op = crm_element_value(change, PCMK_XA_OPERATION);
         xpath = crm_element_value(change, PCMK_XA_PATH);
 
         crm_trace("Continue performing %s on %s with %p", op, xpath, match);
 
         if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
             int position = 0;
             xmlNode *child = NULL;
             xmlNode *match_child = NULL;
 
             match_child = match->children;
             crm_element_value_int(change, PCMK_XE_POSITION, &position);
 
             while ((match_child != NULL)
                    && (position != pcmk__xml_position(match_child, pcmk__xf_skip))) {
                 match_child = match_child->next;
             }
 
             child = xmlDocCopyNode(change->children, match->doc, 1);
             if (child == NULL) {
                 return ENOMEM;
             }
 
             if (match_child) {
                 crm_trace("Adding %s at position %d", child->name, position);
                 xmlAddPrevSibling(match_child, child);
 
             } else if (match->last) {
                 crm_trace("Adding %s at position %d (end)",
                           child->name, position);
                 xmlAddNextSibling(match->last, child);
 
             } else {
                 crm_trace("Adding %s at position %d (first)",
                           child->name, position);
                 CRM_LOG_ASSERT(position == 0);
                 xmlAddChild(match, child);
             }
-            pcmk__mark_xml_created(child);
+            pcmk__xml_mark_created(child);
 
         } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
             int position = 0;
 
             crm_element_value_int(change, PCMK_XE_POSITION, &position);
             if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
                 xmlNode *match_child = NULL;
                 int p = position;
 
                 if (p > pcmk__xml_position(match, pcmk__xf_skip)) {
                     p++; // Skip ourselves
                 }
 
                 CRM_ASSERT(match->parent != NULL);
                 match_child = match->parent->children;
 
                 while ((match_child != NULL)
                        && (p != pcmk__xml_position(match_child, pcmk__xf_skip))) {
                     match_child = match_child->next;
                 }
 
                 crm_trace("Moving %s to position %d (was %d, prev %p, %s %p)",
                           match->name, position,
                           pcmk__xml_position(match, pcmk__xf_skip),
                           match->prev, (match_child? "next":"last"),
                           (match_child? match_child : match->parent->last));
 
                 if (match_child) {
                     xmlAddPrevSibling(match_child, match);
 
                 } else {
                     CRM_ASSERT(match->parent->last != NULL);
                     xmlAddNextSibling(match->parent->last, match);
                 }
 
             } else {
                 crm_trace("%s is already in position %d",
                           match->name, position);
             }
 
             if (position != pcmk__xml_position(match, pcmk__xf_skip)) {
                 crm_err("Moved %s.%s to position %d instead of %d (%p)",
                         match->name, pcmk__xe_id(match),
                         pcmk__xml_position(match, pcmk__xf_skip),
                         position, match->prev);
                 rc = pcmk_rc_diff_failed;
             }
         }
     }
 
     g_list_free_full(change_objs, free);
     return rc;
 }
 
 int
 xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
 {
     int format = 1;
     int rc = pcmk_ok;
     xmlNode *old = NULL;
     const char *digest = NULL;
 
     if (patchset == NULL) {
         return rc;
     }
 
     pcmk__log_xml_patchset(LOG_TRACE, patchset);
 
     if (check_version) {
         rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
         if (rc != pcmk_ok) {
             return rc;
         }
     }
 
     digest = crm_element_value(patchset, PCMK__XA_DIGEST);
     if (digest != NULL) {
         /* Make original XML available for logging in case result doesn't have
          * expected digest
          */
         pcmk__if_tracing(old = pcmk__xml_copy(NULL, xml), {});
     }
 
     if (rc == pcmk_ok) {
         crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
         switch (format) {
             case 1:
                 // @COMPAT Remove when v1 patchsets are removed
                 rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset));
                 break;
             case 2:
                 rc = pcmk_rc2legacy(apply_v2_patchset(xml, patchset));
                 break;
             default:
                 crm_err("Unknown patch format: %d", format);
                 rc = -EINVAL;
         }
     }
 
     if ((rc == pcmk_ok) && (digest != NULL)) {
         char *new_digest = NULL;
         char *version = crm_element_value_copy(xml, PCMK_XA_CRM_FEATURE_SET);
 
         new_digest = calculate_xml_versioned_digest(xml, FALSE, TRUE, version);
         if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
             crm_info("v%d digest mis-match: expected %s, calculated %s",
                      format, digest, new_digest);
             rc = -pcmk_err_diff_failed;
             pcmk__if_tracing(
                 {
                     save_xml_to_file(old, "PatchDigest:input", NULL);
                     save_xml_to_file(xml, "PatchDigest:result", NULL);
                     save_xml_to_file(patchset, "PatchDigest:diff", NULL);
                 },
                 {}
             );
 
         } else {
             crm_trace("v%d digest matched: expected %s, calculated %s",
                       format, digest, new_digest);
         }
         free(new_digest);
         free(version);
     }
     free_xml(old);
     return rc;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 static bool
 can_prune_leaf_v1(xmlNode *node)
 {
     xmlNode *cIter = NULL;
     bool can_prune = true;
 
     CRM_CHECK(node != NULL, return false);
 
     /* @COMPAT PCMK__XE_ROLE_REF was deprecated in Pacemaker 1.1.12 (needed for
      * rolling upgrades)
      */
     if (pcmk__strcase_any_of((const char *) node->name,
                              PCMK_XE_RESOURCE_REF, PCMK_XE_OBJ_REF,
                              PCMK_XE_ROLE, PCMK__XE_ROLE_REF,
                              NULL)) {
         return false;
     }
 
     for (xmlAttrPtr a = pcmk__xe_first_attr(node); a != NULL; a = a->next) {
         const char *p_name = (const char *) a->name;
 
         if (strcmp(p_name, PCMK_XA_ID) == 0) {
             continue;
         }
         can_prune = false;
     }
 
     cIter = pcmk__xml_first_child(node);
     while (cIter) {
         xmlNode *child = cIter;
 
         cIter = pcmk__xml_next(cIter);
         if (can_prune_leaf_v1(child)) {
             free_xml(child);
         } else {
             can_prune = false;
         }
     }
     return can_prune;
 }
 
 // @COMPAT Remove when v1 patchsets are removed
 xmlNode *
 pcmk__diff_v1_xml_object(xmlNode *old, xmlNode *new, bool suppress)
 {
     xmlNode *tmp1 = NULL;
     xmlNode *diff = pcmk__xe_create(NULL, PCMK_XE_DIFF);
     xmlNode *removed = pcmk__xe_create(diff, PCMK__XE_DIFF_REMOVED);
     xmlNode *added = pcmk__xe_create(diff, PCMK__XE_DIFF_ADDED);
 
     crm_xml_add(diff, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
 
     tmp1 = subtract_v1_xml_object(removed, old, new, false, NULL,
                                   "removed:top");
     if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
         free_xml(tmp1);
     }
 
     tmp1 = subtract_v1_xml_object(added, new, old, true, NULL, "added:top");
     if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
         free_xml(tmp1);
     }
 
     if ((added->children == NULL) && (removed->children == NULL)) {
         free_xml(diff);
         diff = NULL;
     }
 
     return diff;
 }
 
 // Deprecated functions kept only for backward API compatibility
 // LCOV_EXCL_START
 
 #include <crm/common/xml_compat.h>
 
 gboolean
 apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
 {
     gboolean result = TRUE;
     int root_nodes_seen = 0;
     const char *digest = crm_element_value(diff, PCMK__XA_DIGEST);
     const char *version = crm_element_value(diff, PCMK_XA_CRM_FEATURE_SET);
 
     xmlNode *child_diff = NULL;
     xmlNode *added = pcmk__xe_first_child(diff, PCMK__XE_DIFF_ADDED, NULL,
                                           NULL);
     xmlNode *removed = pcmk__xe_first_child(diff, PCMK__XE_DIFF_REMOVED, NULL,
                                             NULL);
 
     CRM_CHECK(new_xml != NULL, return FALSE);
 
     crm_trace("Subtraction Phase");
     for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
          child_diff = pcmk__xml_next(child_diff)) {
         CRM_CHECK(root_nodes_seen == 0, result = FALSE);
         if (root_nodes_seen == 0) {
             *new_xml = subtract_v1_xml_object(NULL, old_xml, child_diff, false,
                                               NULL, NULL);
         }
         root_nodes_seen++;
     }
 
     if (root_nodes_seen == 0) {
         *new_xml = pcmk__xml_copy(NULL, old_xml);
 
     } else if (root_nodes_seen > 1) {
         crm_err("(-) Diffs cannot contain more than one change set... saw %d",
                 root_nodes_seen);
         result = FALSE;
     }
 
     root_nodes_seen = 0;
     crm_trace("Addition Phase");
     if (result) {
         xmlNode *child_diff = NULL;
 
         for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
              child_diff = pcmk__xml_next(child_diff)) {
             CRM_CHECK(root_nodes_seen == 0, result = FALSE);
             if (root_nodes_seen == 0) {
                 pcmk__xml_update(NULL, *new_xml, child_diff, true);
             }
             root_nodes_seen++;
         }
     }
 
     if (root_nodes_seen > 1) {
         crm_err("(+) Diffs cannot contain more than one change set... saw %d",
                 root_nodes_seen);
         result = FALSE;
 
     } else if (result && (digest != NULL)) {
         char *new_digest = NULL;
 
         purge_v1_diff_markers(*new_xml);    // Purge now so diff is ok
         new_digest = calculate_xml_versioned_digest(*new_xml, FALSE, TRUE,
                                                     version);
         if (!pcmk__str_eq(new_digest, digest, pcmk__str_casei)) {
             crm_info("Digest mis-match: expected %s, calculated %s",
                      digest, new_digest);
             result = FALSE;
 
             pcmk__if_tracing(
                 {
                     save_xml_to_file(old_xml, "diff:original", NULL);
                     save_xml_to_file(diff, "diff:input", NULL);
                     save_xml_to_file(*new_xml, "diff:new", NULL);
                 },
                 {}
             );
 
         } else {
             crm_trace("Digest matched: expected %s, calculated %s",
                       digest, new_digest);
         }
         free(new_digest);
 
     } else if (result) {
         purge_v1_diff_markers(*new_xml);    // Purge now so diff is ok
     }
 
     return result;
 }
 
 void
 purge_diff_markers(xmlNode *a_node)
 {
     purge_v1_diff_markers(a_node);
 }
 
 xmlNode *
 diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
 {
     return pcmk__diff_v1_xml_object(old, new, suppress);
 }
 
 xmlNode *
 subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
                     gboolean full, gboolean *changed, const char *marker)
 {
     return subtract_v1_xml_object(parent, left, right, full, changed, marker);
 }
 
 gboolean
 can_prune_leaf(xmlNode *xml_node)
 {
     return can_prune_leaf_v1(xml_node);
 }
 
 // LCOV_EXCL_STOP
 // End deprecated API
diff --git a/lib/common/xml.c b/lib/common/xml.c
index 4a81bdeac0..190857b4df 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -1,2582 +1,2630 @@
 /*
  * Copyright 2004-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>                   // stat(), S_ISREG, etc.
 #include <sys/types.h>
 
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
 #include <crm/crm.h>
 #include <crm/common/xml.h>
 #include <crm/common/xml_internal.h>    // 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);
 }
 
 /*!
  * \internal
  * \brief Clear flags on an XML node
  *
  * \param[in,out] xml        XML node whose flags to reset
  * \param[in,out] user_data  Ignored
  *
  * \return \c true (to continue traversing the tree)
  *
  * \note This is compatible with \c pcmk__xml_tree_foreach().
  */
 static bool
 reset_xml_node_flags(xmlNode *xml, void *user_data)
 {
     xml_node_private_t *nodepriv = xml->_private;
 
     if (nodepriv != NULL) {
         nodepriv->flags = pcmk__xf_none;
     }
     return true;
 }
 
-// Set xpf_created flag on XML node and any children
-void
-pcmk__mark_xml_created(xmlNode *xml)
+/*!
+ * \internal
+ * \brief Set the \c pcmk__xf_dirty and \c pcmk__xf_created flags on an XML node
+ *
+ * \param[in,out] xml        Node whose flags to set
+ * \param[in]     user_data  Ignored
+ *
+ * \return \c true (to continue traversing the tree)
+ *
+ * \note This is compatible with \c pcmk__xml_tree_foreach().
+ */
+static bool
+mark_xml_dirty_created(xmlNode *xml, void *user_data)
 {
-    xmlNode *cIter = NULL;
-    xml_node_private_t *nodepriv = NULL;
+    xml_node_private_t *nodepriv = xml->_private;
 
+    if (nodepriv != NULL) {
+        pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_created);
+    }
+    return true;
+}
+
+/*!
+ * \internal
+ * \brief Mark an XML tree as dirty and created, and mark its parents dirty
+ *
+ * Also mark the document dirty.
+ *
+ * \param[in,out] xml  Tree to mark as dirty and created
+ */
+void
+pcmk__xml_mark_created(xmlNode *xml)
+{
     CRM_ASSERT(xml != NULL);
-    nodepriv = xml->_private;
 
-    if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
-        if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
-            pcmk__set_xml_flags(nodepriv, pcmk__xf_created);
-            pcmk__mark_xml_node_dirty(xml);
-        }
-        for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
-             cIter = pcmk__xml_next(cIter)) {
-            pcmk__mark_xml_created(cIter);
-        }
+    if (!pcmk__tracking_xml_changes(xml, false)) {
+        // Tracking is disabled for entire document
+        return;
     }
+
+    // Mark all parents and document dirty
+    pcmk__mark_xml_node_dirty(xml);
+
+    pcmk__xml_tree_foreach(xml, mark_xml_dirty_created, NULL);
 }
 
 #define XML_DOC_PRIVATE_MAGIC   0x81726354UL
 #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;
 }
 
 /*!
  * \internal
  * \brief Remove all attributes marked as deleted from an XML node
  *
  * \param[in,out] xml        XML node whose deleted attributes to remove
  * \param[in,out] user_data  Ignored
  *
  * \return \c true (to continue traversing the tree)
  *
  * \note This is compatible with \c pcmk__xml_tree_foreach().
  */
 static bool
 accept_attr_deletions(xmlNode *xml, void *user_data)
 {
     reset_xml_node_flags(xml, NULL);
     pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL);
     return true;
 }
 
 /*!
  * \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;
     pcmk__xml_tree_foreach(top, accept_attr_deletions, NULL);
 }
 
 /*!
  * \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 = "<null>";
 
     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 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 a named attribute from an XML element
+ *
+ * This is a wrapper for \c pcmk__xe_remove_attr() for use with
+ * \c pcmk__xml_tree_foreach().
+ *
+ * \param[in,out] xml        XML element to remove an attribute from
+ * \param[in]     user_data  Name of attribute to remove
+ *
+ * \return \c true (to continue traversing the tree)
+ *
+ * \note This is compatible with \c pcmk__xml_tree_foreach().
+ */
+bool
+pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
+{
+    const char *name = user_data;
+
+    pcmk__xe_remove_attr(xml, name);
+    return true;
+}
+
 /*!
  * \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);
+
+    pcmk__xml_mark_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  <tt>printf(3)</tt>-style format string
  * \param[in]     ...     Arguments for \p format
  *
  * \note This function escapes special characters. \c xmlNodeSetContent() does
  *       not.
  */
 G_GNUC_PRINTF(2, 3)
 void
 pcmk__xe_set_content(xmlNode *node, const char *format, ...)
 {
     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);
+    pcmk__xml_mark_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 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') {
         switch (type) {
             case pcmk__xml_escape_text:
                 switch (*text) {
                     case '<':
                     case '>':
                     case '&':
                         return true;
                     case '\n':
                     case '\t':
                         break;
                     default:
                         if (g_ascii_iscntrl(*text)) {
                             return true;
                         }
                         break;
                 }
                 break;
 
             case pcmk__xml_escape_attr:
                 switch (*text) {
                     case '<':
                     case '>':
                     case '&':
                     case '"':
                         return true;
                     default:
                         if (g_ascii_iscntrl(*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 = g_utf8_next_char(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
         if ((*text & 0x80) != 0) {
             size_t bytes = g_utf8_next_char(text) - text;
 
             g_string_append_len(copy, text, bytes);
             text += bytes;
             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 (g_ascii_iscntrl(*text)) {
                             g_string_append_printf(copy, "&#x%.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 (g_ascii_iscntrl(*text)) {
                             g_string_append_printf(copy, "&#x%.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 = g_utf8_next_char(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
     pcmk__xml_tree_foreach(candidate, reset_xml_node_flags, NULL);
 
     // 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), "<no id>"),
               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)
 {
     xml_node_private_t *nodepriv = NULL;
 
     CRM_CHECK(new_xml != NULL, return);
     if (old_xml == NULL) {
-        pcmk__mark_xml_created(new_xml);
+        pcmk__xml_mark_created(new_xml);
         pcmk__apply_creation_acl(new_xml, check_top);
         return;
     }
 
     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 (xmlNode *old_child = pcmk__xml_first_child(old_xml); old_child != NULL;
          old_child = pcmk__xml_next(old_child)) {
 
         xmlNode *new_child = pcmk__xml_match(new_xml, old_child, true);
 
         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 (xmlNode *new_child = pcmk__xml_first_child(new_xml); new_child != NULL;
          new_child = pcmk__xml_next(new_child)) {
 
         xmlNode *old_child = pcmk__xml_match(old_xml, new_child, true);
 
         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, "<null>"),
                   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, "<null>"),
                   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, "<null>"),
                   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, "<null>"),
               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
     pcmk__xml_tree_foreach(new, reset_xml_node_flags, NULL);
 
     old = xmlReplaceNode(old, new);
 
     if (xml_tracking_changes(new)) {
         // Replaced sections may have included relevant ACLs
         pcmk__apply_acl(new);
     }
     xml_calculate_changes(old, new);
     xmlFreeNode(old);
 }
 
 /*!
  * \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 <crm/common/xml_compat.h>
 
 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);
+    pcmk__xml_mark_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, "&lt;");
                 break;
             case '>':
                 copy = replace_text(copy, &index, &length, "&gt;");
                 break;
             case '"':
                 copy = replace_text(copy, &index, &length, "&quot;");
                 break;
             case '\'':
                 copy = replace_text(copy, &index, &length, "&apos;");
                 break;
             case '&':
                 copy = replace_text(copy, &index, &length, "&amp;");
                 break;
             case '\t':
                 /* Might as well just expand to a few spaces... */
                 copy = replace_text(copy, &index, &length, "    ");
                 break;
             case '\n':
                 copy = replace_text(copy, &index, &length, "\\n");
                 break;
             case '\r':
                 copy = replace_text(copy, &index, &length, "\\r");
                 break;
             default:
                 /* Check for and replace non-printing characters with their octal equivalent */
                 if(copy[index] < ' ' || copy[index] > '~') {
                     char *replace = crm_strdup_printf("\\%.3o", copy[index]);
 
                     copy = replace_text(copy, &index, &length, replace);
                     free(replace);
                 }
         }
     }
     return copy;
 }
 
 xmlNode *
 copy_xml(xmlNode *src)
 {
     xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
     xmlNode *copy = NULL;
 
     pcmk__mem_assert(doc);
 
     copy = xmlDocCopyNode(src, doc, 1);
     pcmk__mem_assert(copy);
 
     xmlDocSetRootElement(doc, copy);
     return copy;
 }
 
 xmlNode *
 create_xml_node(xmlNode *parent, const char *name)
 {
     // Like pcmk__xe_create(), but returns NULL on failure
     xmlNode *node = NULL;
 
     CRM_CHECK(!pcmk__str_empty(name), return NULL);
 
     if (parent == NULL) {
         xmlDoc *doc = xmlNewDoc(PCMK__XML_VERSION);
 
         if (doc == NULL) {
             return NULL;
         }
 
         node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
         if (node == NULL) {
             xmlFreeDoc(doc);
             return NULL;
         }
         xmlDocSetRootElement(doc, node);
 
     } else {
         node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
         if (node == NULL) {
             return NULL;
         }
     }
-    pcmk__mark_xml_created(node);
+    pcmk__xml_mark_created(node);
     return node;
 }
 
 xmlNode *
 pcmk_create_xml_text_node(xmlNode *parent, const char *name,
                           const char *content)
 {
     xmlNode *node = pcmk__xe_create(parent, name);
 
     pcmk__xe_set_content(node, "%s", content);
     return node;
 }
 
 xmlNode *
 pcmk_create_html_node(xmlNode *parent, const char *element_name, const char *id,
                       const char *class_name, const char *text)
 {
     xmlNode *node = pcmk__html_create(parent, element_name, id, class_name);
 
     pcmk__xe_set_content(node, "%s", text);
     return node;
 }
 
 xmlNode *
 first_named_child(const xmlNode *parent, const char *name)
 {
     return pcmk__xe_first_child(parent, name, NULL, NULL);
 }
 
 xmlNode *
 find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
 {
     xmlNode *result = NULL;
 
     if (search_path == NULL) {
         crm_warn("Will never find <NULL>");
         return NULL;
     }
 
     result = pcmk__xe_first_child(root, search_path, NULL, NULL);
 
     if (must_find && (result == NULL)) {
         crm_warn("Could not find %s in %s",
                  search_path,
                  ((root != NULL)? (const char *) root->name : "<NULL>"));
     }
 
     return result;
 }
 
 xmlNode *
 crm_next_same_xml(const xmlNode *sibling)
 {
     return pcmk__xe_next_same(sibling);
 }
 
 void
 xml_remove_prop(xmlNode * obj, const char *name)
 {
     pcmk__xe_remove_attr(obj, name);
 }
 
 gboolean
 replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean delete_only)
 {
     bool is_match = false;
     const char *child_id = NULL;
     const char *update_id = NULL;
 
     CRM_CHECK(child != NULL, return FALSE);
     CRM_CHECK(update != NULL, return FALSE);
 
     child_id = pcmk__xe_id(child);
     update_id = pcmk__xe_id(update);
 
     /* Match element name and (if provided in update XML) element ID. Don't
      * match search root (child is search root if parent == NULL).
      */
     is_match = (parent != NULL)
                && pcmk__xe_is(update, (const char *) child->name)
                && ((update_id == NULL)
                    || pcmk__str_eq(update_id, child_id, pcmk__str_none));
 
     /* For deletion, match all attributes provided in update. A matching node
      * can have additional attributes, but values must match for provided ones.
      */
     if (is_match && delete_only) {
         for (xmlAttr *attr = pcmk__xe_first_attr(update); attr != NULL;
              attr = attr->next) {
             const char *name = (const char *) attr->name;
             const char *update_val = pcmk__xml_attr_value(attr);
             const char *child_val = crm_element_value(child, name);
 
             if (!pcmk__str_eq(update_val, child_val, pcmk__str_casei)) {
                 is_match = false;
                 break;
             }
         }
     }
 
     if (is_match) {
         if (delete_only) {
             crm_log_xml_trace(child, "delete-match");
             crm_log_xml_trace(update, "delete-search");
             free_xml(child);
 
         } else {
             crm_log_xml_trace(child, "replace-match");
             crm_log_xml_trace(update, "replace-with");
             replace_node(child, update);
         }
         return TRUE;
     }
 
     // Current node not a match; search the rest of the subtree depth-first
     parent = child;
     for (child = pcmk__xml_first_child(parent); child != NULL;
          child = pcmk__xml_next(child)) {
 
         // Only delete/replace the first match
         if (replace_xml_child(parent, child, update, delete_only)) {
             return TRUE;
         }
     }
 
     // No match found in this subtree
     return FALSE;
 }
 
 gboolean
 update_xml_child(xmlNode *child, xmlNode *to_update)
 {
     return pcmk__xe_update_match(child, to_update) == pcmk_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