13 #include <sys/types.h> 21 #include <libxml/tree.h> 29 static xmlNode *subtract_xml_comment(xmlNode *
parent, xmlNode *left,
30 xmlNode *right, gboolean *changed);
36 add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
38 xmlNode *cIter = NULL;
39 xmlAttr *pIter = NULL;
40 xmlNode *change = NULL;
42 const char *value = NULL;
57 g_string_free(xpath, TRUE);
64 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
65 pIter = pIter->next) {
68 nodepriv = pIter->_private;
83 g_string_free(xpath, TRUE);
107 for (pIter = pcmk__xe_first_attr(xml); pIter != NULL;
108 pIter = pIter->next) {
109 nodepriv = pIter->_private;
118 for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
119 cIter = pcmk__xml_next(cIter)) {
120 add_xml_changes_to_patchset(cIter, patchset);
123 nodepriv = xml->_private;
137 g_string_free(xpath, TRUE);
143 is_config_change(xmlNode *xml)
151 nodepriv = config->_private;
157 if ((xml->doc != NULL) && (xml->doc->_private != NULL)) {
158 docpriv = xml->doc->_private;
159 for (gIter = docpriv->
deleted_objs; gIter; gIter = gIter->next) {
162 if (strstr(deleted_obj->
path,
172 xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
177 xmlNode *diff_child = NULL;
179 const char *tag = NULL;
181 const char *vfields[] = {
187 if (local_diff == NULL) {
192 tag =
"diff-removed";
194 if (diff_child == NULL) {
204 for (lpc = 0; (last != NULL) && (lpc <
PCMK__NELEM(vfields)); lpc++) {
208 if (changed || lpc == 2) {
215 if (diff_child == NULL) {
225 for (lpc = 0; next && lpc <
PCMK__NELEM(vfields); lpc++) {
231 for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
234 xmlSetProp(cib, a->name, (
pcmkXmlStr) p_value);
241 xml_create_patchset_v1(xmlNode *source, xmlNode *
target,
bool config,
248 xml_repair_v1_diff(source,
target, patchset, config);
255 xml_create_patchset_v2(xmlNode *source, xmlNode *
target)
263 xmlNode *patchset = NULL;
264 const char *vfields[] = {
276 docpriv =
target->doc->_private;
303 for (gIter = docpriv->
deleted_objs; gIter; gIter = gIter->next) {
314 add_xml_changes_to_patchset(
target, patchset);
320 bool *config_changed,
bool manage_version)
324 xmlNode *patch = NULL;
333 config = is_config_change(
target);
334 if (config_changed) {
335 *config_changed = config;
338 if (manage_version && config) {
345 }
else if (manage_version) {
347 crm_trace(
"Status changed %d - %d %s", format, counter,
363 patch = xml_create_patchset_v1(source,
target, config, FALSE);
366 patch = xml_create_patchset_v2(source,
target);
369 crm_err(
"Unknown patch format: %d", format);
383 if ((patch == NULL) || (source == NULL) || (
target == NULL)) {
393 if ((format > 1) && !with_digest) {
408 not_id(xmlAttrPtr attr,
void *user_data)
410 return strcmp((
const char *) attr->name,
XML_ATTR_ID) != 0;
415 process_v1_removals(xmlNode *
target, xmlNode *patch)
417 xmlNode *patch_child = NULL;
418 xmlNode *cIter = NULL;
421 const char *
name = NULL;
422 const char *value = NULL;
424 if ((
target == NULL) || (patch == NULL)) {
428 if (
target->type == XML_COMMENT_NODE) {
436 CRM_CHECK(pcmk__str_eq(crm_element_name(
target), crm_element_name(patch),
444 if ((value != NULL) && (strcmp(value,
"removed:top") == 0)) {
445 crm_trace(
"We are the root of the deletion: %s.id=%s",
name,
id);
455 cIter = pcmk__xml_first_child(
target);
457 xmlNode *target_child = cIter;
459 cIter = pcmk__xml_next(cIter);
461 process_v1_removals(target_child, patch_child);
468 process_v1_additions(xmlNode *
parent, xmlNode *
target, xmlNode *patch)
470 xmlNode *patch_child = NULL;
471 xmlNode *target_child = NULL;
472 xmlAttrPtr xIter = NULL;
474 const char *
id = NULL;
475 const char *
name = NULL;
476 const char *value = NULL;
486 if ((
target == NULL) && (value != NULL)
487 && (strcmp(value,
"added:top") == 0)) {
489 name = crm_element_name(patch);
490 crm_trace(
"We are the root of the addition: %s.id=%s",
name,
id);
494 }
else if (
target == NULL) {
496 name = crm_element_name(patch);
501 if (
target->type == XML_COMMENT_NODE) {
507 CRM_CHECK(pcmk__str_eq(crm_element_name(
target), crm_element_name(patch),
512 for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
513 xIter = xIter->next) {
514 const char *p_name = (
const char *) xIter->name;
522 for (patch_child = pcmk__xml_first_child(patch); patch_child != NULL;
523 patch_child = pcmk__xml_next(patch_child)) {
526 process_v1_additions(
target, target_child, patch_child);
542 find_patch_xml_node(
const xmlNode *patchset,
int format,
bool added,
543 xmlNode **patch_node)
550 label = added?
"diff-added" :
"diff-removed";
553 if (cib_node != NULL) {
554 *patch_node = cib_node;
558 label = added?
"target" :
"source";
563 crm_warn(
"Unknown patch format: %d", format);
578 const char *vfields[] = {
588 if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
594 crm_trace(
"Got %d for del[%s]", del[lpc], vfields[lpc]);
599 if (!find_patch_xml_node(patchset, format, TRUE, &tmp)) {
605 crm_trace(
"Got %d for add[%s]", add[lpc], vfields[lpc]);
622 xml_patch_version_check(
const xmlNode *xml,
const xmlNode *patchset,
int format)
625 bool changed = FALSE;
627 int this[] = { 0, 0, 0 };
628 int add[] = { 0, 0, 0 };
629 int del[] = { 0, 0, 0 };
631 const char *vfields[] = {
639 crm_trace(
"Got %d for this[%s]",
this[lpc], vfields[lpc]);
648 add[2] =
this[2] + 1;
650 del[lpc] =
this[lpc];
656 if (
this[lpc] < del[lpc]) {
657 crm_debug(
"Current %s is too low (%d.%d.%d < %d.%d.%d --> %d.%d.%d)",
658 vfields[lpc],
this[0],
this[1],
this[2],
659 del[0], del[1], del[2], add[0], add[1], add[2]);
662 }
else if (
this[lpc] > del[lpc]) {
663 crm_info(
"Current %s is too high (%d.%d.%d > %d.%d.%d --> %d.%d.%d) %p",
664 vfields[lpc],
this[0],
this[1],
this[2],
665 del[0], del[1], del[2], add[0], add[1], add[2], patchset);
672 if (add[lpc] > del[lpc]) {
678 crm_notice(
"Versions did not change in patch %d.%d.%d",
679 add[0], add[1], add[2]);
683 crm_debug(
"Can apply patch %d.%d.%d to %d.%d.%d",
684 add[0], add[1], add[2],
this[0],
this[1],
this[2]);
698 apply_v1_patchset(xmlNode *xml,
const xmlNode *patchset)
701 int root_nodes_seen = 0;
703 xmlNode *child_diff = NULL;
704 xmlNode *added =
find_xml_node(patchset,
"diff-added", FALSE);
705 xmlNode *removed =
find_xml_node(patchset,
"diff-removed", FALSE);
709 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
710 child_diff = pcmk__xml_next(child_diff)) {
711 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
712 if (root_nodes_seen == 0) {
713 process_v1_removals(xml, child_diff);
718 if (root_nodes_seen > 1) {
719 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d",
727 xmlNode *child_diff = NULL;
729 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
730 child_diff = pcmk__xml_next(child_diff)) {
731 CRM_CHECK(root_nodes_seen == 0, rc = FALSE);
732 if (root_nodes_seen == 0) {
733 process_v1_additions(NULL, xml, child_diff);
739 if (root_nodes_seen > 1) {
740 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d",
753 first_matching_xml_child(
const xmlNode *
parent,
const char *
name,
754 const char *
id,
int position)
756 xmlNode *cIter = NULL;
758 for (cIter = pcmk__xml_first_child(
parent); cIter != NULL;
759 cIter = pcmk__xml_next(cIter)) {
760 if (strcmp((
const char *) cIter->name,
name) != 0) {
763 const char *cid =
ID(cIter);
765 if ((cid == NULL) || (strcmp(cid,
id) != 0)) {
771 if ((cIter->type == XML_COMMENT_NODE)
796 search_v2_xpath(
const xmlNode *top,
const char *key,
int target_position)
798 xmlNode *
target = (xmlNode *) top->doc;
799 const char *current = key;
809 key_len = strlen(key);
815 remainder = calloc(key_len,
sizeof(
char));
818 section = calloc(key_len,
sizeof(
char));
821 id = calloc(key_len,
sizeof(
char));
824 tag = calloc(key_len,
sizeof(
char));
829 rc = sscanf(current,
"/%[^/]%s", section, remainder);
832 int f = sscanf(section,
"%[^[][@" XML_ATTR_ID "='%[^']", tag,
id);
833 int current_position = -1;
838 if ((rc == 1) && (target_position >= 0)) {
839 current_position = target_position;
860 }
while ((rc == 2) &&
target);
864 (
path = (
char *) xmlGetNodePath(
target)), key);
877 typedef struct xml_change_obj_s {
878 const xmlNode *change;
883 sort_change_obj_by_position(gconstpointer a, gconstpointer b)
893 if (position_a < position_b) {
896 }
else if (position_a > position_b) {
913 apply_v2_patchset(xmlNode *xml,
const xmlNode *patchset)
916 const xmlNode *change = NULL;
917 GList *change_objs = NULL;
920 for (change = pcmk__xml_first_child(patchset); change != NULL;
921 change = pcmk__xml_next(change)) {
922 xmlNode *match = NULL;
931 crm_trace(
"Processing %s %s", change->name, op);
934 if (strcmp(op,
"delete") == 0) {
937 match = search_v2_xpath(xml, xpath, position);
938 crm_trace(
"Performing %s on %s with %p", op, xpath, match);
940 if ((match == NULL) && (strcmp(op,
"delete") == 0)) {
941 crm_debug(
"No %s match for %s in %p", op, xpath, xml->doc);
944 }
else if (match == NULL) {
945 crm_err(
"No %s match for %s in %p", op, xpath, xml->doc);
949 }
else if ((strcmp(op,
"create") == 0) || (strcmp(op,
"move") == 0)) {
955 change_obj->change = change;
956 change_obj->match = match;
958 change_objs = g_list_append(change_objs, change_obj);
960 if (strcmp(op,
"move") == 0) {
962 if ((match->parent != NULL) && (match->parent->last != NULL)) {
963 xmlAddNextSibling(match->parent->last, match);
967 }
else if (strcmp(op,
"delete") == 0) {
970 }
else if (strcmp(op,
"modify") == 0) {
971 xmlNode *attrs = NULL;
981 for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
982 pIter = pIter->next) {
983 const char *
name = (
const char *) pIter->name;
990 crm_err(
"Unknown operation: %s", op);
996 change_objs = g_list_sort(change_objs, sort_change_obj_by_position);
998 for (gIter = change_objs; gIter; gIter = gIter->next) {
1000 xmlNode *match = change_obj->match;
1001 const char *op = NULL;
1002 const char *xpath = NULL;
1004 change = change_obj->change;
1009 crm_trace(
"Continue performing %s on %s with %p", op, xpath, match);
1011 if (strcmp(op,
"create") == 0) {
1013 xmlNode *child = NULL;
1014 xmlNode *match_child = NULL;
1016 match_child = match->children;
1019 while ((match_child != NULL)
1021 match_child = match_child->next;
1024 child = xmlDocCopyNode(change->children, match->doc, 1);
1026 crm_trace(
"Adding %s at position %d", child->name, position);
1027 xmlAddPrevSibling(match_child, child);
1029 }
else if (match->last) {
1030 crm_trace(
"Adding %s at position %d (end)",
1031 child->name, position);
1032 xmlAddNextSibling(match->last, child);
1035 crm_trace(
"Adding %s at position %d (first)",
1036 child->name, position);
1038 xmlAddChild(match, child);
1042 }
else if (strcmp(op,
"move") == 0) {
1047 xmlNode *match_child = NULL;
1055 match_child = match->parent->children;
1057 while ((match_child != NULL)
1059 match_child = match_child->next;
1062 crm_trace(
"Moving %s to position %d (was %d, prev %p, %s %p)",
1063 match->name, position,
1065 match->prev, (match_child?
"next":
"last"),
1066 (match_child? match_child : match->parent->last));
1069 xmlAddPrevSibling(match_child, match);
1073 xmlAddNextSibling(match->parent->last, match);
1077 crm_trace(
"%s is already in position %d",
1078 match->name, position);
1082 crm_err(
"Moved %s.%s to position %d instead of %d (%p)",
1083 match->name,
ID(match),
1085 position, match->prev);
1091 g_list_free_full(change_objs, free);
1100 xmlNode *old = NULL;
1103 if (patchset == NULL) {
1115 rc = logger_out->
message(logger_out,
"xml-patchset", patchset);
1125 if (check_version) {
1126 rc =
pcmk_rc2legacy(xml_patch_version_check(xml, patchset, format));
1146 crm_err(
"Unknown patch format: %d", format);
1151 if ((rc ==
pcmk_ok) && (digest != NULL)) {
1152 char *new_digest = NULL;
1157 crm_info(
"v%d digest mis-match: expected %s, calculated %s",
1158 format, digest, new_digest);
1170 crm_trace(
"v%d digest matched: expected %s, calculated %s",
1171 format, digest, new_digest);
1183 xmlNode *child = NULL;
1188 for (child = pcmk__xml_first_child(a_node); child != NULL;
1189 child = pcmk__xml_next(child)) {
1197 xmlNode *tmp1 = NULL;
1214 if ((added->children == NULL) && (removed->children == NULL)) {
1223 subtract_xml_comment(xmlNode *
parent, xmlNode *left, xmlNode *right,
1227 CRM_CHECK(left->type == XML_COMMENT_NODE,
return NULL);
1229 if ((right == NULL) || !pcmk__str_eq((
const char *)left->content,
1230 (
const char *)right->content,
1232 xmlNode *deleted = NULL;
1245 gboolean full, gboolean *changed,
const char *marker)
1247 gboolean
dummy = FALSE;
1248 xmlNode *diff = NULL;
1249 xmlNode *right_child = NULL;
1250 xmlNode *left_child = NULL;
1251 xmlAttrPtr xIter = NULL;
1253 const char *
id = NULL;
1254 const char *
name = NULL;
1255 const char *value = NULL;
1256 const char *right_val = NULL;
1258 if (changed == NULL) {
1266 if (left->type == XML_COMMENT_NODE) {
1267 return subtract_xml_comment(
parent, left, right, changed);
1271 if (right == NULL) {
1272 xmlNode *deleted = NULL;
1275 crm_element_name(left),
id);
1283 name = crm_element_name(left);
1285 CRM_CHECK(pcmk__str_eq(crm_element_name(left), crm_element_name(right),
1291 if ((value != NULL) && (strcmp(value,
"removed:top") == 0)) {
1292 crm_trace(
"We are the root of the deletion: %s.id=%s",
name,
id);
1301 for (left_child = pcmk__xml_first_child(left); left_child != NULL;
1302 left_child = pcmk__xml_next(left_child)) {
1303 gboolean child_changed = FALSE;
1308 if (child_changed) {
1317 xmlAttrPtr pIter = NULL;
1319 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1320 pIter = pIter->next) {
1321 const char *p_name = (
const char *)pIter->name;
1322 const char *p_value = pcmk__xml_attr_value(pIter);
1332 for (xIter = pcmk__xe_first_attr(left); xIter != NULL;
1333 xIter = xIter->next) {
1334 const char *prop_name = (
const char *) xIter->name;
1335 xmlAttrPtr right_attr = NULL;
1348 right_attr = xmlHasProp(right, (
pcmkXmlStr) prop_name);
1350 nodepriv = right_attr->_private;
1358 xmlAttrPtr pIter = NULL;
1360 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1361 pIter = pIter->next) {
1362 const char *p_name = (
const char *) pIter->name;
1363 const char *p_value = pcmk__xml_attr_value(pIter);
1380 if (strcmp(left_value, right_val) == 0) {
1386 xmlAttrPtr pIter = NULL;
1390 prop_name, crm_element_name(left),
id);
1391 for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
1392 pIter = pIter->next) {
1393 const char *p_name = (
const char *) pIter->name;
1394 const char *p_value = pcmk__xml_attr_value(pIter);
1402 crm_trace(
"Changes detected to %s (%s -> %s) in " 1404 prop_name, left_value, right_val,
1405 crm_element_name(left),
id);
1416 }
else if (!full && (
id != NULL)) {
1432 int root_nodes_seen = 0;
1436 xmlNode *child_diff = NULL;
1438 xmlNode *removed =
find_xml_node(diff,
"diff-removed", FALSE);
1440 CRM_CHECK(new_xml != NULL,
return FALSE);
1443 for (child_diff = pcmk__xml_first_child(removed); child_diff != NULL;
1444 child_diff = pcmk__xml_next(child_diff)) {
1446 if (root_nodes_seen == 0) {
1453 if (root_nodes_seen == 0) {
1456 }
else if (root_nodes_seen > 1) {
1457 crm_err(
"(-) Diffs cannot contain more than one change set... saw %d",
1462 root_nodes_seen = 0;
1465 xmlNode *child_diff = NULL;
1467 for (child_diff = pcmk__xml_first_child(added); child_diff != NULL;
1468 child_diff = pcmk__xml_next(child_diff)) {
1470 if (root_nodes_seen == 0) {
1477 if (root_nodes_seen > 1) {
1478 crm_err(
"(+) Diffs cannot contain more than one change set... saw %d",
1482 }
else if (
result && (digest != NULL)) {
1483 char *new_digest = NULL;
1489 crm_info(
"Digest mis-match: expected %s, calculated %s",
1490 digest, new_digest);
1503 crm_trace(
"Digest matched: expected %s, calculated %s",
1504 digest, new_digest);
#define CRM_CHECK(expr, failure_action)
#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)
#define XML_ATTR_NUMUPDATES
int pcmk_rc2legacy(int rc)
int(* message)(pcmk__output_t *out, const char *message_id,...)
xmlNode * first_named_child(const xmlNode *parent, const char *name)
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.
xmlNode * find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
#define XML_NVPAIR_ATTR_NAME
#define CRM_LOG_ASSERT(expr)
void pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Deprecated Pacemaker XML API.
#define XML_ATTR_GENERATION
xmlNode * copy_xml(xmlNode *src_node)
#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.
void pcmk__xe_remove_matching_attrs(xmlNode *element, bool(*match)(xmlAttrPtr, void *), void *user_data)
#define crm_trace(fmt, args...)
#define crm_log_xml_explicit(xml, text)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Wrappers for and extensions to libxml2.
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
bool xml_document_dirty(xmlNode *xml)
xmlNode * create_xml_node(xmlNode *parent, const char *name)
#define XML_DIFF_POSITION
void purge_diff_markers(xmlNode *a_node)
void free_xml(xmlNode *child)
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)
void pcmk__output_free(pcmk__output_t *out)
const xmlChar * pcmkXmlStr
G_GNUC_INTERNAL bool pcmk__xa_filterable(const char *name)
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...)
void xml_remove_prop(xmlNode *obj, const char *name)
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
gboolean can_prune_leaf(xmlNode *xml_node)
void xml_acl_disable(xmlNode *xml)
This structure contains everything that makes up a single output formatter.
int compare_version(const char *version1, const char *version2)
#define crm_log_xml_info(xml, text)
#define XML_ATTR_GENERATION_ADMIN
#define XML_NVPAIR_ATTR_VALUE
#define XML_ATTR_CRM_VERSION
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
GString * pcmk__element_xpath(const xmlNode *xml)
#define XML_CIB_TAG_CONFIGURATION
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)
G_GNUC_INTERNAL void pcmk__mark_xml_created(xmlNode *xml)
G_GNUC_INTERNAL void pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update, bool as_diff)
#define crm_info(fmt, args...)
int pcmk__log_output_new(pcmk__output_t **out)