This source file includes following definitions.
- print_patch
- apply_patch
- log_patch_cib_versions
- strip_patch_cib_version
- generate_patch
- main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <crm_internal.h>
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/param.h>
28 #include <sys/types.h>
29
30 #include <crm/crm.h>
31 #include <crm/msg_xml.h>
32 #include <crm/common/xml.h>
33 #include <crm/common/ipc.h>
34 #include <crm/cib.h>
35
36
37 static struct crm_option long_options[] = {
38
39 {"help", 0, 0, '?', "\t\tThis text"},
40 {"version", 0, 0, '$', "\t\tVersion information" },
41 {"verbose", 0, 0, 'V', "\t\tIncrease debug output\n"},
42
43 {"-spacer-", 1, 0, '-', "\nOriginal XML:"},
44 {"original", 1, 0, 'o', "\tXML is contained in the named file"},
45 {"original-string", 1, 0, 'O', "XML is contained in the supplied string"},
46
47 {"-spacer-", 1, 0, '-', "\nOperation:"},
48 {"new", 1, 0, 'n', "\tCompare the original XML to the contents of the named file"},
49 {"new-string", 1, 0, 'N', "\tCompare the original XML to the contents of the supplied string"},
50 {"patch", 1, 0, 'p', "\tPatch the original XML with the contents of the named file"},
51
52 {"-spacer-", 1, 0, '-', "\nAdditional Options:"},
53 {"cib", 0, 0, 'c', "\t\tCompare/patch the inputs as a CIB (includes versions details)"},
54 {"stdin", 0, 0, 's', NULL, 1},
55 {"no-version", 0, 0, 'u', "\tGenerate the difference without versions details"},
56 {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph},
57 {"-spacer-", 1, 0, '-', "Obtain the two different configuration files by running cibadmin on the two cluster setups to compare:", pcmk_option_paragraph},
58 {"-spacer-", 1, 0, '-', " cibadmin --query > cib-old.xml", pcmk_option_example},
59 {"-spacer-", 1, 0, '-', " cibadmin --query > cib-new.xml", pcmk_option_example},
60 {"-spacer-", 1, 0, '-', "Calculate and save the difference between the two files:", pcmk_option_paragraph},
61 {"-spacer-", 1, 0, '-', " crm_diff --original cib-old.xml --new cib-new.xml > patch.xml", pcmk_option_example },
62 {"-spacer-", 1, 0, '-', "Apply the patch to the original file:", pcmk_option_paragraph },
63 {"-spacer-", 1, 0, '-', " crm_diff --original cib-old.xml --patch patch.xml > updated.xml", pcmk_option_example },
64 {"-spacer-", 1, 0, '-', "Apply the patch to the running cluster:", pcmk_option_paragraph },
65 {"-spacer-", 1, 0, '-', " cibadmin --patch patch.xml", pcmk_option_example },
66
67 {0, 0, 0, 0}
68 };
69
70
71 static void
72 print_patch(xmlNode *patch)
73 {
74 char *buffer = dump_xml_formatted(patch);
75
76 printf("%s\n", crm_str(buffer));
77 free(buffer);
78 fflush(stdout);
79 }
80
81 static int
82 apply_patch(xmlNode *input, xmlNode *patch, gboolean as_cib)
83 {
84 int rc;
85 xmlNode *output = copy_xml(input);
86
87 rc = xml_apply_patchset(output, patch, as_cib);
88 if (rc != pcmk_ok) {
89 fprintf(stderr, "Could not apply patch: %s\n", pcmk_strerror(rc));
90 free_xml(output);
91 return rc;
92 }
93
94 if (output != NULL) {
95 const char *version;
96 char *buffer;
97
98 print_patch(output);
99
100 version = crm_element_value(output, XML_ATTR_CRM_VERSION);
101 buffer = calculate_xml_versioned_digest(output, FALSE, TRUE, version);
102 crm_trace("Digest: %s\n", crm_str(buffer));
103 free(buffer);
104 free_xml(output);
105 }
106 return pcmk_ok;
107 }
108
109 static void
110 log_patch_cib_versions(xmlNode *patch)
111 {
112 int add[] = { 0, 0, 0 };
113 int del[] = { 0, 0, 0 };
114
115 const char *fmt = NULL;
116 const char *digest = NULL;
117
118 xml_patch_versions(patch, add, del);
119 fmt = crm_element_value(patch, "format");
120 digest = crm_element_value(patch, XML_ATTR_DIGEST);
121
122 if (add[2] != del[2] || add[1] != del[1] || add[0] != del[0]) {
123 crm_info("Patch: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
124 crm_info("Patch: +++ %d.%d.%d %s", add[0], add[1], add[2], digest);
125 }
126 }
127
128 static void
129 strip_patch_cib_version(xmlNode *patch, const char **vfields, size_t nvfields)
130 {
131 int format = 1;
132
133 crm_element_value_int(patch, "format", &format);
134 if (format == 2) {
135 xmlNode *version_xml = find_xml_node(patch, "version", FALSE);
136
137 if (version_xml) {
138 free_xml(version_xml);
139 }
140
141 } else {
142 int i = 0;
143
144 const char *tags[] = {
145 XML_TAG_DIFF_REMOVED,
146 XML_TAG_DIFF_ADDED,
147 };
148
149 for (i = 0; i < DIMOF(tags); i++) {
150 xmlNode *tmp = NULL;
151 int lpc;
152
153 tmp = find_xml_node(patch, tags[i], FALSE);
154 if (tmp) {
155 for (lpc = 0; lpc < nvfields; lpc++) {
156 xml_remove_prop(tmp, vfields[lpc]);
157 }
158
159 tmp = find_xml_node(tmp, XML_TAG_CIB, FALSE);
160 if (tmp) {
161 for (lpc = 0; lpc < nvfields; lpc++) {
162 xml_remove_prop(tmp, vfields[lpc]);
163 }
164 }
165 }
166 }
167 }
168 }
169
170 static int
171 generate_patch(xmlNode *object_1, xmlNode *object_2, const char *xml_file_2,
172 gboolean as_cib, gboolean no_version)
173 {
174 xmlNode *output = NULL;
175
176 const char *vfields[] = {
177 XML_ATTR_GENERATION_ADMIN,
178 XML_ATTR_GENERATION,
179 XML_ATTR_NUMUPDATES,
180 };
181
182
183
184 if (no_version) {
185 int lpc;
186
187 for (lpc = 0; lpc < DIMOF(vfields); lpc++) {
188 crm_copy_xml_element(object_1, object_2, vfields[lpc]);
189 }
190 }
191
192 xml_track_changes(object_2, NULL, object_2, FALSE);
193 xml_calculate_changes(object_1, object_2);
194 crm_log_xml_debug(object_2, (xml_file_2? xml_file_2: "target"));
195
196 output = xml_create_patchset(0, object_1, object_2, NULL, FALSE);
197
198 xml_log_changes(LOG_INFO, __FUNCTION__, object_2);
199 xml_accept_changes(object_2);
200
201 if (output == NULL) {
202 return pcmk_ok;
203 }
204
205 patchset_process_digest(output, object_1, object_2, as_cib);
206
207 if (as_cib) {
208 log_patch_cib_versions(output);
209
210 } else if (no_version) {
211 strip_patch_cib_version(output, vfields, DIMOF(vfields));
212 }
213
214 xml_log_patchset(LOG_NOTICE, __FUNCTION__, output);
215 print_patch(output);
216 free_xml(output);
217 return 1;
218 }
219
220 int
221 main(int argc, char **argv)
222 {
223 gboolean apply = FALSE;
224 gboolean raw_1 = FALSE;
225 gboolean raw_2 = FALSE;
226 gboolean use_stdin = FALSE;
227 gboolean as_cib = FALSE;
228 gboolean no_version = FALSE;
229 int argerr = 0;
230 int flag;
231 int rc = pcmk_ok;
232 xmlNode *object_1 = NULL;
233 xmlNode *object_2 = NULL;
234 const char *xml_file_1 = NULL;
235 const char *xml_file_2 = NULL;
236
237 int option_index = 0;
238
239 crm_log_cli_init("crm_diff");
240 crm_set_options(NULL, "original_xml operation [options]", long_options,
241 "crm_diff can compare two Pacemaker configurations (in XML format) to\n"
242 "produce a custom diff-like output, or apply such an output as a patch\n");
243
244 if (argc < 2) {
245 crm_help('?', EX_USAGE);
246 }
247
248 while (1) {
249 flag = crm_get_option(argc, argv, &option_index);
250 if (flag == -1)
251 break;
252
253 switch (flag) {
254 case 'o':
255 xml_file_1 = optarg;
256 break;
257 case 'O':
258 xml_file_1 = optarg;
259 raw_1 = TRUE;
260 break;
261 case 'n':
262 xml_file_2 = optarg;
263 break;
264 case 'N':
265 xml_file_2 = optarg;
266 raw_2 = TRUE;
267 break;
268 case 'p':
269 xml_file_2 = optarg;
270 apply = TRUE;
271 break;
272 case 's':
273 use_stdin = TRUE;
274 break;
275 case 'c':
276 as_cib = TRUE;
277 break;
278 case 'u':
279 no_version = TRUE;
280 break;
281 case 'V':
282 crm_bump_log_level(argc, argv);
283 break;
284 case '?':
285 case '$':
286 crm_help(flag, EX_OK);
287 break;
288 default:
289 printf("Argument %c (0%o) is not (yet?) supported\n", flag, flag);
290 ++argerr;
291 break;
292 }
293 }
294
295 if (optind < argc) {
296 printf("non-option ARGV-elements: ");
297 while (optind < argc)
298 printf("%s ", argv[optind++]);
299 printf("\n");
300 }
301
302 if (optind > argc) {
303 ++argerr;
304 }
305
306 if (argerr) {
307 crm_help('?', EX_USAGE);
308 }
309
310 if (apply && no_version) {
311 fprintf(stderr, "warning: -u/--no-version ignored with -p/--patch\n");
312 } else if (as_cib && no_version) {
313 fprintf(stderr, "error: -u/--no-version incompatible with -c/--cib\n");
314 return 1;
315 }
316
317 if (raw_1) {
318 object_1 = string2xml(xml_file_1);
319
320 } else if (use_stdin) {
321 fprintf(stderr, "Input first XML fragment:");
322 object_1 = stdin2xml();
323
324 } else if (xml_file_1 != NULL) {
325 object_1 = filename2xml(xml_file_1);
326 }
327
328 if (raw_2) {
329 object_2 = string2xml(xml_file_2);
330
331 } else if (use_stdin) {
332 fprintf(stderr, "Input second XML fragment:");
333 object_2 = stdin2xml();
334
335 } else if (xml_file_2 != NULL) {
336 object_2 = filename2xml(xml_file_2);
337 }
338
339 if (object_1 == NULL) {
340 fprintf(stderr, "Could not parse the first XML fragment\n");
341 return 1;
342 }
343 if (object_2 == NULL) {
344 fprintf(stderr, "Could not parse the second XML fragment\n");
345 return 1;
346 }
347
348 if (apply) {
349 rc = apply_patch(object_1, object_2, as_cib);
350 } else {
351 rc = generate_patch(object_1, object_2, xml_file_2, as_cib, no_version);
352 }
353
354 free_xml(object_1);
355 free_xml(object_2);
356 return rc;
357 }