13 #include <sys/types.h> 21 #include <libxml/tree.h> 32 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
34 xmlNode *cIter = NULL;
35 xmlAttr *pIter = NULL;
36 xmlNode *change = NULL;
38 const char *value = NULL;
40 if (nodepriv == NULL) {
61 g_string_free(xpath, TRUE);
68 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
69 pIter = pIter->next) {
72 nodepriv = pIter->_private;
87 g_string_free(xpath, TRUE);
100 value = pcmk__xml_attr_value(pIter);
111 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
112 pIter = pIter->next) {
113 nodepriv = pIter->_private;
122 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
123 cIter = pcmk__xml_next(cIter)) {
124 add_xml_changes_to_patchset(cIter, patchset);
127 nodepriv = xml->_private;
132 xml->name, pcmk__xe_id(xml),
142 g_string_free(xpath, TRUE);
148 is_config_change(xmlNode *xml)
157 nodepriv = config->_private;
163 if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
164 docpriv = xml->doc->_private;
165 for (gIter = docpriv->
deleted_objs; gIter; gIter = gIter->next) {
168 if (strstr(deleted_obj->
path,
179 xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
184 xmlNode *diff_child = NULL;
186 const char *tag = NULL;
188 const char *vfields[] = {
194 if (local_diff == NULL) {
201 if (diff_child == NULL) {
211 for (lpc = 0; (last != NULL) && (lpc <
PCMK__NELEM(vfields)); lpc++) {
215 if (changed || lpc == 2) {
222 if (diff_child == NULL) {
232 for (lpc = 0; next && lpc <
PCMK__NELEM(vfields); lpc++) {
238 for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
240 const char *p_value = pcmk__xml_attr_value(a);
242 xmlSetProp(cib, a->name, (
pcmkXmlStr) p_value);
250 xml_create_patchset_v1(xmlNode *source, xmlNode *
target,
bool config,
257 xml_repair_v1_diff(source,
target, patchset, config);
264 xml_create_patchset_v2(xmlNode *source, xmlNode *
target)
272 xmlNode *patchset = NULL;
273 const char *vfields[] = {
285 docpriv =
target->doc->_private;
312 for (gIter = docpriv->
deleted_objs; gIter; gIter = gIter->next) {
323 add_xml_changes_to_patchset(
target, patchset);
329 bool *config_changed,
bool manage_version)
333 xmlNode *patch = NULL;
342 config = is_config_change(
target);
343 if (config_changed) {
344 *config_changed = config;
347 if (manage_version && config) {
354 }
else if (manage_version) {
356 crm_trace(
"Status changed %d - %d %s", format, counter,
373 patch = xml_create_patchset_v1(source,
target, config, FALSE);
376 patch = xml_create_patchset_v2(source,
target);
379 crm_err(
"Unknown patch format: %d", format);
393 if ((patch == NULL) || (source == NULL) || (
target == NULL)) {
403 if ((format > 1) && !with_digest) {
418 subtract_v1_xml_comment(xmlNode *
parent, xmlNode *left, xmlNode *right,
422 CRM_CHECK(left->type == XML_COMMENT_NODE,
return NULL);
424 if ((right == NULL) || !pcmk__str_eq((
const char *)left->content,
425 (
const char *)right->content,
427 xmlNode *deleted = NULL;
440 subtract_v1_xml_object(xmlNode *
parent, xmlNode *left, xmlNode *right,
441 bool full, gboolean *changed,
const char *marker)
443 gboolean
dummy = FALSE;
444 xmlNode *diff = NULL;
445 xmlNode *right_child = NULL;
446 xmlNode *left_child = NULL;
447 xmlAttrPtr xIter = NULL;
449 const char *
id = NULL;
450 const char *
name = NULL;
451 const char *value = NULL;
452 const char *right_val = NULL;
454 if (changed == NULL) {
462 if (left->type == XML_COMMENT_NODE) {
463 return subtract_v1_xml_comment(
parent, left, right, changed);
466 id = pcmk__xe_id(left);
467 name = (
const char *) left->name;
469 xmlNode *deleted = NULL;
481 CRM_CHECK(pcmk__xe_is(left, (
const char *) right->name),
return NULL);
485 if ((value != NULL) && (strcmp(value,
"removed:top") == 0)) {
486 crm_trace(
"We are the root of the deletion: %s.id=%s",
name,
id);
495 for (left_child = pcmk__xml_first_child(left); left_child != NULL;
496 left_child = pcmk__xml_next(left_child)) {
497 gboolean child_changed = FALSE;
500 subtract_v1_xml_object(diff, left_child, right_child, full,
501 &child_changed, marker);
511 xmlAttrPtr pIter = NULL;
513 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
514 pIter = pIter->next) {
515 const char *p_name = (
const char *)pIter->name;
516 const char *p_value = pcmk__xml_attr_value(pIter);
526 for (xIter = pcmk__xe_first_attr(left); xIter != NULL;
527 xIter = xIter->next) {
528 const char *prop_name = (
const char *) xIter->name;
529 xmlAttrPtr right_attr = NULL;
542 right_attr = xmlHasProp(right, (
pcmkXmlStr) prop_name);
544 nodepriv = right_attr->_private;
552 xmlAttrPtr pIter = NULL;
554 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
555 pIter = pIter->next) {
556 const char *p_name = (
const char *) pIter->name;
557 const char *p_value = pcmk__xml_attr_value(pIter);
564 const char *left_value = pcmk__xml_attr_value(xIter);
572 const char *left_value = pcmk__xml_attr_value(xIter);
574 if (strcmp(left_value, right_val) == 0) {
580 xmlAttrPtr pIter = NULL;
584 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
585 pIter = pIter->next) {
586 const char *p_name = (
const char *) pIter->name;
587 const char *p_value = pcmk__xml_attr_value(pIter);
595 crm_trace(
"Changes detected to %s (%s -> %s) in " 597 prop_name, left_value, right_val,
name,
id);
608 }
else if (!full && (
id != NULL)) {
620 not_id(xmlAttrPtr attr,
void *user_data)
622 return strcmp((
const char *) attr->name,
PCMK_XA_ID) != 0;
630 process_v1_removals(xmlNode *
target, xmlNode *patch)
632 xmlNode *patch_child = NULL;
633 xmlNode *cIter = NULL;
636 const char *value = NULL;
638 if ((
target == NULL) || (patch == NULL)) {
642 if (
target->type == XML_COMMENT_NODE) {
656 if ((value != NULL) && (strcmp(value,
"removed:top") == 0)) {
657 crm_trace(
"We are the root of the deletion: %s.id=%s",
668 cIter = pcmk__xml_first_child(
target);
670 xmlNode *target_child = cIter;
672 cIter = pcmk__xml_next(cIter);
674 process_v1_removals(target_child, patch_child);
684 process_v1_additions(xmlNode *
parent, xmlNode *
target, xmlNode *patch)
686 xmlNode *patch_child = NULL;
687 xmlNode *target_child = NULL;
688 xmlAttrPtr xIter = NULL;
690 const char *
id = NULL;
691 const char *
name = NULL;
692 const char *value = NULL;
701 name = (
const char *) patch->name;
703 if ((
target == NULL) && (value != NULL)
704 && (strcmp(value,
"added:top") == 0)) {
705 id = pcmk__xe_id(patch);
706 crm_trace(
"We are the root of the addition: %s.id=%s",
name,
id);
710 }
else if (
target == NULL) {
711 id = pcmk__xe_id(patch);
716 if (
target->type == XML_COMMENT_NODE) {
725 for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
726 xIter = xIter->next) {
727 const char *p_name = (
const char *) xIter->name;
728 const char *p_value = pcmk__xml_attr_value(xIter);
735 for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
736 patch_child = pcmk__xml_next(patch_child)) {
739 process_v1_additions(
target, target_child, patch_child);
755 find_patch_xml_node(
const xmlNode *patchset,
int format,
bool added,
756 xmlNode **patch_node)
768 if (cib_node != NULL) {
769 *patch_node = cib_node;
779 crm_warn(
"Unknown patch format: %d", format);
794 const char *vfields[] = {
804 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
810 crm_trace(
"Got %d for del[%s]", del[lpc], vfields[lpc]);
815 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
821 crm_trace(
"Got %d for add[%s]", add[lpc], vfields[lpc]);
837 xml_patch_version_check(
const xmlNode *xml,
const xmlNode *patchset)
840 bool changed = FALSE;
842 int this[] = { 0, 0, 0 };
843 int add[] = { 0, 0, 0 };
844 int del[] = { 0, 0, 0 };
846 const char *vfields[] = {
854 crm_trace(
"Got %d for this[%s]",
this[lpc], vfields[lpc]);
863 add[2] =
this[2] + 1;
865 del[lpc] =
this[lpc];
871 if (
this[lpc] < del[lpc]) {
872 crm_debug(
"Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
873 vfields[lpc],
this[0],
this[1],
this[2],
874 del[0], del[1], del[2], add[0], add[1], add[2]);
877 }
else if (
this[lpc] > del[lpc]) {
878 crm_info(
"Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
879 vfields[lpc],
this[0],
this[1],
this[2],
880 del[0], del[1], del[2], add[0], add[1], add[2], patchset);
887 if (add[lpc] > del[lpc]) {
893 crm_notice(
"Versions did not change in patch %d.%d.%d",
894 add[0], add[1], add[2]);
898 crm_debug(
"Can apply patch %d.%d.%d to %d.%d.%d",
899 add[0], add[1], add[2],
this[0],
this[1],
this[2]);
905 purge_v1_diff_markers(xmlNode *node)
907 xmlNode *child = NULL;
912 for (child = pcmk__xml_first_child(node); child != NULL;
913 child = pcmk__xml_next(child)) {
914 purge_v1_diff_markers(child);
929 apply_v1_patchset(xmlNode *xml,
const xmlNode *patchset)
932 int root_nodes_seen = 0;
934 xmlNode *child_diff = NULL;
942 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
943 child_diff = pcmk__xml_next(child_diff)) {
944 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
945 if (root_nodes_seen == 0) {
946 process_v1_removals(xml, child_diff);
951 if (root_nodes_seen > 1) {
952 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d",
960 xmlNode *child_diff = NULL;
962 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
963 child_diff = pcmk__xml_next(child_diff)) {
964 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
965 if (root_nodes_seen == 0) {
966 process_v1_additions(NULL, xml, child_diff);
972 if (root_nodes_seen > 1) {
973 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d",
978 purge_v1_diff_markers(xml);
986 first_matching_xml_child(
const xmlNode *
parent,
const char *
name,
987 const char *
id,
int position)
989 xmlNode *cIter = NULL;
991 for (cIter = pcmk__xml_first_child(
parent); cIter != NULL;
992 cIter = pcmk__xml_next(cIter)) {
993 if (strcmp((
const char *) cIter->name,
name) != 0) {
996 const char *cid = pcmk__xe_id(cIter);
998 if ((cid == NULL) || (strcmp(cid,
id) != 0)) {
1004 if ((cIter->type == XML_COMMENT_NODE)
1029 search_v2_xpath(
const xmlNode *top,
const char *key,
int target_position)
1031 xmlNode *
target = (xmlNode *) top->doc;
1032 const char *current = key;
1042 key_len = strlen(key);
1055 rc = sscanf(current,
"/%[^/]%s", section, remainder);
1058 int f = sscanf(section,
"%[^[][@" PCMK_XA_ID "='%[^']", tag,
id);
1059 int current_position = -1;
1064 if ((rc == 1) && (target_position >= 0)) {
1065 current_position = target_position;
1083 current = remainder;
1087 }
while ((rc == 2) &&
target);
1091 (
path = (
char *) xmlGetNodePath(
target)), key);
1104 typedef struct xml_change_obj_s {
1105 const xmlNode *change;
1110 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
1114 int position_a = -1;
1115 int position_b = -1;
1120 if (position_a < position_b) {
1123 }
else if (position_a > position_b) {
1140 apply_v2_patchset(xmlNode *xml,
const xmlNode *patchset)
1143 const xmlNode *change = NULL;
1144 GList *change_objs = NULL;
1145 GList *gIter = NULL;
1147 for (change = pcmk__xml_first_child(patchset); change != NULL;
1148 change = pcmk__xml_next(change)) {
1149 xmlNode *match = NULL;
1158 crm_trace(
"Processing %s %s", change->name, op);
1166 match = search_v2_xpath(xml, xpath, position);
1167 crm_trace(
"Performing %s on %s with %p", op, xpath, match);
1170 crm_debug(
"No %s match for %s in %p", op, xpath, xml->doc);
1173 }
else if (match == NULL) {
1174 crm_err(
"No %s match for %s in %p", op, xpath, xml->doc);
1184 change_obj->change = change;
1185 change_obj->match = match;
1187 change_objs = g_list_append(change_objs, change_obj);
1191 if ((match->parent != NULL) && (match->parent->last != NULL)) {
1192 xmlAddNextSibling(match->parent->last, match);
1203 const xmlNode *attrs = pcmk__xml_first_child(child);
1205 if (attrs == NULL) {
1211 for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
1212 pIter = pIter->next) {
1213 const char *
name = (
const char *) pIter->name;
1214 const char *value = pcmk__xml_attr_value(pIter);
1220 crm_err(
"Unknown operation: %s", op);
1226 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
1228 for (gIter = change_objs; gIter; gIter = gIter->next) {
1230 xmlNode *match = change_obj->match;
1231 const char *op = NULL;
1232 const char *xpath = NULL;
1234 change = change_obj->change;
1239 crm_trace(
"Continue performing %s on %s with %p", op, xpath, match);
1243 xmlNode *child = NULL;
1244 xmlNode *match_child = NULL;
1246 match_child = match->children;
1249 while ((match_child != NULL)
1251 match_child = match_child->next;
1254 child = xmlDocCopyNode(change->children, match->doc, 1);
1255 if (child == NULL) {
1260 crm_trace(
"Adding %s at position %d", child->name, position);
1261 xmlAddPrevSibling(match_child, child);
1263 }
else if (match->last) {
1264 crm_trace(
"Adding %s at position %d (end)",
1265 child->name, position);
1266 xmlAddNextSibling(match->last, child);
1269 crm_trace(
"Adding %s at position %d (first)",
1270 child->name, position);
1272 xmlAddChild(match, child);
1281 xmlNode *match_child = NULL;
1289 match_child = match->parent->children;
1291 while ((match_child != NULL)
1293 match_child = match_child->next;
1296 crm_trace(
"Moving %s to position %d (was %d, prev %p, %s %p)",
1297 match->name, position,
1299 match->prev, (match_child?
"next":
"last"),
1300 (match_child? match_child : match->parent->last));
1303 xmlAddPrevSibling(match_child, match);
1307 xmlAddNextSibling(match->parent->last, match);
1311 crm_trace(
"%s is already in position %d",
1312 match->name, position);
1316 crm_err(
"Moved %s.%s to position %d instead of %d (%p)",
1317 match->name, pcmk__xe_id(match),
1319 position, match->prev);
1325 g_list_free_full(change_objs, free);
1334 xmlNode *old = NULL;
1335 const char *digest = NULL;
1337 if (patchset == NULL) {
1343 if (check_version) {
1351 if (digest != NULL) {
1369 crm_err(
"Unknown patch format: %d", format);
1374 if ((rc ==
pcmk_ok) && (digest != NULL)) {
1375 char *new_digest = NULL;
1380 crm_info(
"v%d digest mis-match: expected %s, calculated %s",
1381 format, digest, new_digest);
1393 crm_trace(
"v%d digest matched: expected %s, calculated %s",
1394 format, digest, new_digest);
1405 can_prune_leaf_v1(xmlNode *node)
1407 xmlNode *cIter = NULL;
1408 bool can_prune =
true;
1422 for (xmlAttrPtr a = pcmk__xe_first_attr(node); a != NULL; a = a->next) {
1423 const char *p_name = (
const char *) a->name;
1431 cIter = pcmk__xml_first_child(node);
1433 xmlNode *child = cIter;
1435 cIter = pcmk__xml_next(cIter);
1436 if (can_prune_leaf_v1(child)) {
1449 xmlNode *tmp1 = NULL;
1456 tmp1 = subtract_v1_xml_object(removed, old,
new,
false, NULL,
1458 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1462 tmp1 = subtract_v1_xml_object(added,
new, old,
true, NULL,
"added:top");
1463 if (suppress && (tmp1 != NULL) && can_prune_leaf_v1(tmp1)) {
1467 if ((added->children == NULL) && (removed->children == NULL)) {
1484 int root_nodes_seen = 0;
1488 xmlNode *child_diff = NULL;
1494 CRM_CHECK(new_xml != NULL,
return FALSE);
1497 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
1498 child_diff = pcmk__xml_next(child_diff)) {
1500 if (root_nodes_seen == 0) {
1501 *new_xml = subtract_v1_xml_object(NULL, old_xml, child_diff,
false,
1507 if (root_nodes_seen == 0) {
1510 }
else if (root_nodes_seen > 1) {
1511 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d",
1516 root_nodes_seen = 0;
1519 xmlNode *child_diff = NULL;
1521 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
1522 child_diff = pcmk__xml_next(child_diff)) {
1524 if (root_nodes_seen == 0) {
1532 if (root_nodes_seen > 1) {
1533 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d",
1537 }
else if (
result && (digest != NULL)) {
1538 char *new_digest = NULL;
1540 purge_v1_diff_markers(*new_xml);
1544 crm_info(
"Digest mis-match: expected %s, calculated %s",
1545 digest, new_digest);
1558 crm_trace(
"Digest matched: expected %s, calculated %s",
1559 digest, new_digest);
1564 purge_v1_diff_markers(*new_xml);
1573 purge_v1_diff_markers(a_node);
1584 gboolean full, gboolean *changed,
const char *marker)
1586 return subtract_v1_xml_object(
parent, left, right, full, changed, marker);
1592 return can_prune_leaf_v1(xml_node);
#define CRM_CHECK(expr, failure_action)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
#define PCMK__XE_DIFF_REMOVED
#define crm_notice(fmt, args...)
void patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target, bool with_digest)
#define pcmk__if_tracing(if_action, else_action)
int pcmk_rc2legacy(int rc)
void save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
#define PCMK_XE_CHANGE_ATTR
#define PCMK_VALUE_CREATE
#define PCMK_XE_RESOURCE_REF
G_GNUC_INTERNAL int pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
#define PCMK_XA_NUM_UPDATES
#define PCMK_XE_CHANGE_LIST
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
#define CRM_LOG_ASSERT(expr)
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
#define PCMK_XA_OPERATION
Deprecated Pacemaker XML API.
#define crm_warn(fmt, args...)
#define pcmk_err_diff_failed
G_GNUC_INTERNAL void pcmk__xc_update(xmlNode *parent, xmlNode *target, xmlNode *update)
#define crm_debug(fmt, args...)
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
struct xml_change_obj_s xml_change_obj_t
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
#define PCMK__XE_DIFF_ADDED
#define PCMK_VALUE_MODIFY
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
G_GNUC_INTERNAL void pcmk__xml_mark_created(xmlNode *xml)
#define PCMK_XE_CHANGE_RESULT
#define crm_trace(fmt, args...)
#define PCMK_VALUE_DELETE
#define crm_log_xml_explicit(xml, text)
#define PCMK__XE_ROLE_REF
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
xmlNode * pcmk__diff_v1_xml_object(xmlNode *old, xmlNode *new, bool suppress)
Wrappers for and extensions to libxml2.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
bool xml_document_dirty(xmlNode *xml)
void purge_diff_markers(xmlNode *a_node)
void free_xml(xmlNode *child)
#define PCMK_XE_CONFIGURATION
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
xmlNode * xml_create_patchset(int format, xmlNode *source, xmlNode *target, bool *config_changed, bool manage_version)
G_GNUC_INTERNAL xmlNode * pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
const xmlChar * pcmkXmlStr
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
#define PCMK_XA_CRM_FEATURE_SET
G_GNUC_INTERNAL void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags, bool as_diff)
gboolean apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
xmlNode * diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
pcmk__action_result_t result
#define crm_err(fmt, args...)
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
void xml_acl_disable(xmlNode *xml)
int compare_version(const char *version1, const char *version2)
#define crm_log_xml_info(xml, text)
#define PCMK__XA_CRM_DIFF_MARKER
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
GString * pcmk__element_xpath(const xmlNode *xml)
#define pcmk__log_xml_patchset(level, patchset)
xmlNode * subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right, gboolean full, gboolean *changed, const char *marker)
int xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
#define PCMK_XA_ADMIN_EPOCH
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
#define pcmk__assert_alloc(nmemb, size)
#define crm_info(fmt, args...)
gboolean can_prune_leaf(xmlNode *xml_node)