pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
patchset_display.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <crm/common/xml.h>
13
14#include "crmcommon_private.h"
15
40static int
41xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset)
42{
43 int rc = pcmk_rc_no_output;
44 int add[] = { 0, 0, 0 };
45 int del[] = { 0, 0, 0 };
46
47 xml_patch_versions(patchset, add, del);
48
49 if ((add[0] != del[0]) || (add[1] != del[1]) || (add[2] != del[2])) {
50 const char *fmt = crm_element_value(patchset, PCMK_XA_FORMAT);
51 const char *digest = crm_element_value(patchset, PCMK__XA_DIGEST);
52
53 out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
54 rc = out->info(out, "Diff: +++ %d.%d.%d %s",
55 add[0], add[1], add[2], digest);
56
57 } else if ((add[0] != 0) || (add[1] != 0) || (add[2] != 0)) {
58 rc = out->info(out, "Local-only Change: %d.%d.%d",
59 add[0], add[1], add[2]);
60 }
61
62 return rc;
63}
64
79static int
80xml_show_patchset_v1_recursive(pcmk__output_t *out, const char *prefix,
81 const xmlNode *data, int depth, uint32_t options)
82{
83 if ((data->children == NULL)
85
86 // Found a change; clear the pcmk__xml_fmt_diff_short option if set
87 options &= ~pcmk__xml_fmt_diff_short;
88
91 } else { // pcmk_is_set(options, pcmk__xml_fmt_diff_minus)
93 }
94 }
95
97 int rc = pcmk_rc_no_output;
98
99 // Keep looking for the actual change
100 for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
101 child = pcmk__xml_next(child)) {
102 int temp_rc = xml_show_patchset_v1_recursive(out, prefix, child,
103 depth + 1, options);
104 rc = pcmk__output_select_rc(rc, temp_rc);
105 }
106 return rc;
107 }
108
109 return pcmk__xml_show(out, prefix, data, depth,
110 options
114}
115
131static int
132xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset,
133 uint32_t options)
134{
135 const xmlNode *removed = NULL;
136 const xmlNode *added = NULL;
137 const xmlNode *child = NULL;
138 bool is_first = true;
139 int rc = xml_show_patchset_header(out, patchset);
140
141 /* It's not clear whether "- " or "+ " ever does *not* get overridden by
142 * PCMK__XML_PREFIX_DELETED or PCMK__XML_PREFIX_CREATED in practice.
143 * However, v1 patchsets can only exist during rolling upgrades from
144 * Pacemaker 1.1.11, so not worth worrying about.
145 */
146 removed = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_REMOVED, NULL, NULL);
147 for (child = pcmk__xml_first_child(removed); child != NULL;
148 child = pcmk__xml_next(child)) {
149 int temp_rc = xml_show_patchset_v1_recursive(out, "- ", child, 0,
150 options
152 rc = pcmk__output_select_rc(rc, temp_rc);
153
154 if (is_first) {
155 is_first = false;
156 } else {
157 rc = pcmk__output_select_rc(rc, out->info(out, " --- "));
158 }
159 }
160
161 is_first = true;
162 added = pcmk__xe_first_child(patchset, PCMK__XE_DIFF_ADDED, NULL, NULL);
163 for (child = pcmk__xml_first_child(added); child != NULL;
164 child = pcmk__xml_next(child)) {
165 int temp_rc = xml_show_patchset_v1_recursive(out, "+ ", child, 0,
166 options
168 rc = pcmk__output_select_rc(rc, temp_rc);
169
170 if (is_first) {
171 is_first = false;
172 } else {
173 rc = pcmk__output_select_rc(rc, out->info(out, " +++ "));
174 }
175 }
176
177 return rc;
178}
179
194static int
195xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset)
196{
197 int rc = xml_show_patchset_header(out, patchset);
198 int temp_rc = pcmk_rc_no_output;
199
200 for (const xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL,
201 NULL);
202 change != NULL; change = pcmk__xe_next(change)) {
203
204 const char *op = crm_element_value(change, PCMK_XA_OPERATION);
205 const char *xpath = crm_element_value(change, PCMK_XA_PATH);
206
207 if (op == NULL) {
208 continue;
209 }
210
211 if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
212 char *prefix = crm_strdup_printf(PCMK__XML_PREFIX_CREATED " %s: ",
213 xpath);
214
215 temp_rc = pcmk__xml_show(out, prefix, change->children, 0,
217 rc = pcmk__output_select_rc(rc, temp_rc);
218
219 // Overwrite all except the first two characters with spaces
220 for (char *ch = prefix + 2; *ch != '\0'; ch++) {
221 *ch = ' ';
222 }
223
224 temp_rc = pcmk__xml_show(out, prefix, change->children, 0,
228 rc = pcmk__output_select_rc(rc, temp_rc);
229 free(prefix);
230
231 } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
232 const char *position = crm_element_value(change, PCMK_XE_POSITION);
233
234 temp_rc = out->info(out,
235 PCMK__XML_PREFIX_MOVED " %s moved to offset %s",
236 xpath, position);
237 rc = pcmk__output_select_rc(rc, temp_rc);
238
239 } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
240 xmlNode *clist = pcmk__xe_first_child(change, PCMK_XE_CHANGE_LIST,
241 NULL, NULL);
242 GString *buffer_set = NULL;
243 GString *buffer_unset = NULL;
244
245 for (const xmlNode *child = pcmk__xe_first_child(clist, NULL, NULL,
246 NULL);
247 child != NULL; child = pcmk__xe_next(child)) {
248
249 const char *name = crm_element_value(child, PCMK_XA_NAME);
250
252 if (op == NULL) {
253 continue;
254 }
255
256 if (strcmp(op, "set") == 0) {
257 const char *value = crm_element_value(child, PCMK_XA_VALUE);
258
259 pcmk__add_separated_word(&buffer_set, 256, "@", ", ");
260 pcmk__g_strcat(buffer_set, name, "=", value, NULL);
261
262 } else if (strcmp(op, "unset") == 0) {
263 pcmk__add_separated_word(&buffer_unset, 256, "@", ", ");
264 g_string_append(buffer_unset, name);
265 }
266 }
267
268 if (buffer_set != NULL) {
269 temp_rc = out->info(out, "+ %s: %s", xpath, buffer_set->str);
270 rc = pcmk__output_select_rc(rc, temp_rc);
271 g_string_free(buffer_set, TRUE);
272 }
273
274 if (buffer_unset != NULL) {
275 temp_rc = out->info(out, "-- %s: %s",
276 xpath, buffer_unset->str);
277 rc = pcmk__output_select_rc(rc, temp_rc);
278 g_string_free(buffer_unset, TRUE);
279 }
280
281 } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
282 int position = -1;
283
284 crm_element_value_int(change, PCMK_XE_POSITION, &position);
285 if (position >= 0) {
286 temp_rc = out->info(out, "-- %s (%d)", xpath, position);
287 } else {
288 temp_rc = out->info(out, "-- %s", xpath);
289 }
290 rc = pcmk__output_select_rc(rc, temp_rc);
291 }
292 }
293
294 return rc;
295}
296
312PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
313static int
314xml_patchset_default(pcmk__output_t *out, va_list args)
315{
316 const xmlNode *patchset = va_arg(args, const xmlNode *);
317
318 int format = 1;
319
320 if (patchset == NULL) {
321 crm_trace("Empty patch");
322 return pcmk_rc_no_output;
323 }
324
325 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
326 switch (format) {
327 case 1:
328 return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty);
329 case 2:
330 return xml_show_patchset_v2(out, patchset);
331 default:
332 crm_err("Unknown patch format: %d", format);
334 }
335}
336
352PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
353static int
354xml_patchset_log(pcmk__output_t *out, va_list args)
355{
356 static struct qb_log_callsite *patchset_cs = NULL;
357
358 const xmlNode *patchset = va_arg(args, const xmlNode *);
359
360 uint8_t log_level = pcmk__output_get_log_level(out);
361 int format = 1;
362
363 if (log_level == LOG_NEVER) {
364 return pcmk_rc_no_output;
365 }
366
367 if (patchset == NULL) {
368 crm_trace("Empty patch");
369 return pcmk_rc_no_output;
370 }
371
372 if (patchset_cs == NULL) {
373 patchset_cs = qb_log_callsite_get(__func__, __FILE__, "xml-patchset",
374 log_level, __LINE__,
376 }
377
378 if (!crm_is_callsite_active(patchset_cs, log_level, crm_trace_nonlog)) {
379 // Nothing would be logged, so skip all the work
380 return pcmk_rc_no_output;
381 }
382
383 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
384 switch (format) {
385 case 1:
386 if (log_level < LOG_DEBUG) {
387 return xml_show_patchset_v1(out, patchset,
390 }
391 return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty);
392 case 2:
393 return xml_show_patchset_v2(out, patchset);
394 default:
395 crm_err("Unknown patch format: %d", format);
397 }
398}
399
415PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
416static int
417xml_patchset_xml(pcmk__output_t *out, va_list args)
418{
419 const xmlNode *patchset = va_arg(args, const xmlNode *);
420
421 if (patchset != NULL) {
422 GString *buf = g_string_sized_new(1024);
423
425 0);
426
427 out->output_xml(out, PCMK_XE_XML_PATCHSET, buf->str);
428 g_string_free(buf, TRUE);
429 return pcmk_rc_ok;
430 }
431 crm_trace("Empty patch");
432 return pcmk_rc_no_output;
433}
434
435static pcmk__message_entry_t fmt_functions[] = {
436 { "xml-patchset", "default", xml_patchset_default },
437 { "xml-patchset", "log", xml_patchset_log },
438 { "xml-patchset", "xml", xml_patchset_xml },
439
440 { NULL, NULL, NULL }
441};
442
449void
453
454// Deprecated functions kept only for backward API compatibility
455// LCOV_EXCL_START
456
458
459void
460xml_log_patchset(uint8_t log_level, const char *function,
461 const xmlNode *patchset)
462{
463 /* This function has some duplication relative to the message functions.
464 * This way, we can maintain the const xmlNode * in the signature. The
465 * message functions must be non-const. They have to support XML output
466 * objects, which must make a copy of a the patchset, requiring a non-const
467 * function call.
468 *
469 * In contrast, this legacy function doesn't need to support XML output.
470 */
471 static struct qb_log_callsite *patchset_cs = NULL;
472
473 pcmk__output_t *out = NULL;
474 int format = 1;
475 int rc = pcmk_rc_no_output;
476
477 switch (log_level) {
478 case LOG_NEVER:
479 return;
480 case LOG_STDOUT:
481 CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return);
482 break;
483 default:
484 if (patchset_cs == NULL) {
485 patchset_cs = qb_log_callsite_get(__func__, __FILE__,
486 "xml-patchset", log_level,
487 __LINE__, crm_trace_nonlog);
488 }
489 if (!crm_is_callsite_active(patchset_cs, log_level,
491 return;
492 }
493 CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return);
494 pcmk__output_set_log_level(out, log_level);
495 break;
496 }
497
498 if (patchset == NULL) {
499 // Should come after the LOG_NEVER check
500 crm_trace("Empty patch");
501 goto done;
502 }
503
504 crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
505 switch (format) {
506 case 1:
507 if (log_level < LOG_DEBUG) {
508 rc = xml_show_patchset_v1(out, patchset,
511 } else { // Note: LOG_STDOUT > LOG_DEBUG
512 rc = xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty);
513 }
514 break;
515 case 2:
516 rc = xml_show_patchset_v2(out, patchset);
517 break;
518 default:
519 crm_err("Unknown patch format: %d", format);
521 break;
522 }
523
524done:
525 out->finish(out, pcmk_rc2exitc(rc), true, NULL);
527}
528
529// LCOV_EXCL_STOP
530// End deprecated API
const char * name
Definition cib.c:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
char data[0]
Definition cpg.c:10
#define PCMK__XML_PREFIX_MOVED
XML has been moved.
#define PCMK__XML_PREFIX_CREATED
XML is newly created.
#define PCMK__XML_PREFIX_DELETED
XML has been deleted.
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
Definition logging.c:680
#define LOG_STDOUT
Definition logging.h:43
unsigned int crm_trace_nonlog
Definition logging.c:46
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define LOG_NEVER
Definition logging.h:48
#define crm_trace(fmt, args...)
Definition logging.h:402
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:482
#define PCMK_VALUE_MOVE
Definition options.h:175
#define PCMK_VALUE_DELETE
Definition options.h:144
#define PCMK_VALUE_MODIFY
Definition options.h:174
#define PCMK_VALUE_CREATE
Definition options.h:139
void pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
Definition output_log.c:390
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition output.c:204
void pcmk__output_free(pcmk__output_t *out)
Definition output.c:30
int pcmk__log_output_new(pcmk__output_t **out)
Definition output.c:291
int pcmk__text_output_new(pcmk__output_t **out, const char *filename)
Definition output.c:320
#define PCMK__OUTPUT_ARGS(ARGS...)
uint8_t pcmk__output_get_log_level(const pcmk__output_t *out)
Definition output_log.c:363
void xml_log_patchset(uint8_t log_level, const char *function, const xmlNode *patchset)
void pcmk__register_patchset_messages(pcmk__output_t *out)
@ pcmk_rc_no_output
Definition results.h:131
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_bad_xml_patch
Definition results.h:121
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition results.c:702
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition strings.c:794
This structure contains everything that makes up a single output formatter.
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Wrappers for and extensions to libxml2.
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
Definition patchset.c:788
Deprecated Pacemaker XML API.
@ pcmk__xml_fmt_close
Include the closing tag of an XML element.
@ pcmk__xml_fmt_diff_plus
Log a created XML subtree.
@ pcmk__xml_fmt_children
Include the children of an XML element.
@ pcmk__xml_fmt_open
Include the opening tag of an XML element, and include XML comments.
@ pcmk__xml_fmt_diff_minus
Log a removed XML subtree.
@ pcmk__xml_fmt_pretty
Include indentation and newlines.
@ pcmk__xml_fmt_text
Include XML text nodes.
@ pcmk__xml_fmt_diff_short
Log a minimal version of an XML diff (only showing the changes)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options)
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition xml_io.c:488
#define PCMK_XA_FORMAT
Definition xml_names.h:286
#define PCMK_XA_OPERATION
Definition xml_names.h:344
#define PCMK_XA_PATH
Definition xml_names.h:350
#define PCMK_XE_CHANGE_LIST
Definition xml_names.h:76
#define PCMK_XA_VALUE
Definition xml_names.h:437
#define PCMK_XE_XML_PATCHSET
Definition xml_names.h:217
#define PCMK_XA_NAME
Definition xml_names.h:325
#define PCMK_XE_POSITION
Definition xml_names.h:159
#define PCMK__XE_DIFF_REMOVED
#define PCMK__XA_CRM_DIFF_MARKER
#define PCMK__XE_DIFF_ADDED
#define PCMK__XA_DIGEST