pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
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 
40 static int
41 xml_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 
79 static int
80 xml_show_patchset(pcmk__output_t *out, const xmlNode *patchset)
81 {
82  int rc = xml_show_patchset_header(out, patchset);
83  int temp_rc = pcmk_rc_no_output;
84 
85  for (const xmlNode *change = pcmk__xe_first_child(patchset, NULL, NULL,
86  NULL);
87  change != NULL; change = pcmk__xe_next(change, NULL)) {
88 
89  const char *op = crm_element_value(change, PCMK_XA_OPERATION);
90  const char *xpath = crm_element_value(change, PCMK_XA_PATH);
91 
92  if (op == NULL) {
93  continue;
94  }
95 
96  if (strcmp(op, PCMK_VALUE_CREATE) == 0) {
97  char *prefix = crm_strdup_printf(PCMK__XML_PREFIX_CREATED " %s: ",
98  xpath);
99 
100  temp_rc = pcmk__xml_show(out, prefix, change->children, 0,
102  rc = pcmk__output_select_rc(rc, temp_rc);
103 
104  // Overwrite all except the first two characters with spaces
105  for (char *ch = prefix + 2; *ch != '\0'; ch++) {
106  *ch = ' ';
107  }
108 
109  temp_rc = pcmk__xml_show(out, prefix, change->children, 0,
113  rc = pcmk__output_select_rc(rc, temp_rc);
114  free(prefix);
115 
116  } else if (strcmp(op, PCMK_VALUE_MOVE) == 0) {
117  const char *position = crm_element_value(change, PCMK_XE_POSITION);
118 
119  temp_rc = out->info(out,
120  PCMK__XML_PREFIX_MOVED " %s moved to offset %s",
121  xpath, position);
122  rc = pcmk__output_select_rc(rc, temp_rc);
123 
124  } else if (strcmp(op, PCMK_VALUE_MODIFY) == 0) {
125  xmlNode *clist = pcmk__xe_first_child(change, PCMK_XE_CHANGE_LIST,
126  NULL, NULL);
127  GString *buffer_set = NULL;
128  GString *buffer_unset = NULL;
129 
130  for (const xmlNode *child = pcmk__xe_first_child(clist, NULL, NULL,
131  NULL);
132  child != NULL; child = pcmk__xe_next(child, NULL)) {
133 
134  const char *name = crm_element_value(child, PCMK_XA_NAME);
135 
137  if (op == NULL) {
138  continue;
139  }
140 
141  if (strcmp(op, "set") == 0) {
142  const char *value = crm_element_value(child, PCMK_XA_VALUE);
143 
144  pcmk__add_separated_word(&buffer_set, 256, "@", ", ");
145  pcmk__g_strcat(buffer_set, name, "=", value, NULL);
146 
147  } else if (strcmp(op, "unset") == 0) {
148  pcmk__add_separated_word(&buffer_unset, 256, "@", ", ");
149  g_string_append(buffer_unset, name);
150  }
151  }
152 
153  if (buffer_set != NULL) {
154  temp_rc = out->info(out, "+ %s: %s", xpath, buffer_set->str);
155  rc = pcmk__output_select_rc(rc, temp_rc);
156  g_string_free(buffer_set, TRUE);
157  }
158 
159  if (buffer_unset != NULL) {
160  temp_rc = out->info(out, "-- %s: %s",
161  xpath, buffer_unset->str);
162  rc = pcmk__output_select_rc(rc, temp_rc);
163  g_string_free(buffer_unset, TRUE);
164  }
165 
166  } else if (strcmp(op, PCMK_VALUE_DELETE) == 0) {
167  int position = -1;
168 
169  crm_element_value_int(change, PCMK_XE_POSITION, &position);
170  if (position >= 0) {
171  temp_rc = out->info(out, "-- %s (%d)", xpath, position);
172  } else {
173  temp_rc = out->info(out, "-- %s", xpath);
174  }
175  rc = pcmk__output_select_rc(rc, temp_rc);
176  }
177  }
178 
179  return rc;
180 }
181 
197 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
198 static int
199 xml_patchset_default(pcmk__output_t *out, va_list args)
200 {
201  const xmlNode *patchset = va_arg(args, const xmlNode *);
202 
203  int format = 1;
204 
205  if (patchset == NULL) {
206  crm_trace("Empty patch");
207  return pcmk_rc_no_output;
208  }
209 
210  crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
211  if (format != 2) {
212  crm_err("Unknown patch format: %d", format);
213  return pcmk_rc_bad_xml_patch;
214  }
215 
216  return xml_show_patchset(out, patchset);
217 }
218 
234 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
235 static int
236 xml_patchset_log(pcmk__output_t *out, va_list args)
237 {
238  static struct qb_log_callsite *patchset_cs = NULL;
239 
240  const xmlNode *patchset = va_arg(args, const xmlNode *);
241 
242  uint8_t log_level = pcmk__output_get_log_level(out);
243  int format = 1;
244 
245  if (log_level == LOG_NEVER) {
246  return pcmk_rc_no_output;
247  }
248 
249  if (patchset == NULL) {
250  crm_trace("Empty patch");
251  return pcmk_rc_no_output;
252  }
253 
254  if (patchset_cs == NULL) {
255  patchset_cs = qb_log_callsite_get(__func__, __FILE__, "xml-patchset",
256  log_level, __LINE__,
258  }
259 
260  if (!crm_is_callsite_active(patchset_cs, log_level, crm_trace_nonlog)) {
261  // Nothing would be logged, so skip all the work
262  return pcmk_rc_no_output;
263  }
264 
265  crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
266  if (format != 2) {
267  crm_err("Unknown patch format: %d", format);
268  return pcmk_rc_bad_xml_patch;
269  }
270 
271  return xml_show_patchset(out, patchset);
272 }
273 
289 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
290 static int
291 xml_patchset_xml(pcmk__output_t *out, va_list args)
292 {
293  const xmlNode *patchset = va_arg(args, const xmlNode *);
294 
295  if (patchset != NULL) {
296  GString *buf = g_string_sized_new(1024);
297 
299  0);
300 
301  out->output_xml(out, PCMK_XE_XML_PATCHSET, buf->str);
302  g_string_free(buf, TRUE);
303  return pcmk_rc_ok;
304  }
305  crm_trace("Empty patch");
306  return pcmk_rc_no_output;
307 }
308 
309 static pcmk__message_entry_t fmt_functions[] = {
310  { "xml-patchset", "default", xml_patchset_default },
311  { "xml-patchset", "log", xml_patchset_log },
312  { "xml-patchset", "xml", xml_patchset_xml },
313 
314  { NULL, NULL, NULL }
315 };
316 
323 void
325  pcmk__register_messages(out, fmt_functions);
326 }
#define PCMK__XA_DIGEST
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
#define PCMK_XA_NAME
Definition: xml_names.h:330
void pcmk__register_messages(pcmk__output_t *out, const pcmk__message_entry_t *table)
Definition: output.c:204
#define PCMK_XA_PATH
Definition: xml_names.h:355
const char * name
Definition: cib.c:26
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition: xml_io.c:411
#define PCMK_VALUE_CREATE
Definition: options.h:140
Include the opening tag of an XML element, and include XML comments.
Definition: xml_internal.h:152
#define PCMK_XE_CHANGE_LIST
Definition: xml_names.h:76
void pcmk__register_patchset_messages(pcmk__output_t *out)
#define PCMK_XA_FORMAT
Definition: xml_names.h:291
#define PCMK__XML_PREFIX_MOVED
XML has been moved.
void pcmk__add_separated_word(GString **list, size_t init_size, const char *word, const char *separator)
Definition: strings.c:796
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
unsigned int crm_trace_nonlog
Definition: logging.c:46
#define LOG_NEVER
Definition: logging.h:48
#define PCMK_XA_OPERATION
Definition: xml_names.h:349
Include indentation and newlines.
Definition: xml_internal.h:149
#define PCMK_VALUE_MOVE
Definition: options.h:176
Include the children of an XML element.
Definition: xml_internal.h:155
#define PCMK_VALUE_MODIFY
Definition: options.h:175
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define crm_trace(fmt, args...)
Definition: logging.h:372
#define PCMK_VALUE_DELETE
Definition: options.h:145
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1297
Wrappers for and extensions to libxml2.
#define PCMK_XA_VALUE
Definition: xml_names.h:442
#define PCMK__XML_PREFIX_CREATED
XML is newly created.
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
gboolean crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
Definition: logging.c:694
#define PCMK_XE_XML_PATCHSET
Definition: xml_names.h:222
int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options)
Definition: xml_display.c:221
Include the closing tag of an XML element.
Definition: xml_internal.h:158
#define crm_err(fmt, args...)
Definition: logging.h:359
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: xml_element.c:1201
This structure contains everything that makes up a single output formatter.
Include XML text nodes.
Definition: xml_internal.h:162
bool xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
Definition: patchset.c:311
uint8_t pcmk__output_get_log_level(const pcmk__output_t *out)
Definition: output_log.c:363
#define PCMK_XE_POSITION
Definition: xml_names.h:163
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1