root/lib/common/patchset_display.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xml_show_patchset_header
  2. xml_show_patchset
  3. PCMK__OUTPUT_ARGS
  4. PCMK__OUTPUT_ARGS
  5. PCMK__OUTPUT_ARGS
  6. pcmk__register_patchset_messages

   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 
  16 /*!
  17  * \internal
  18  * \brief Output an XML patchset header
  19  *
  20  * This function parses a header from an XML patchset (a \c PCMK_XE_DIFF element
  21  * and its children).
  22  *
  23  * All header lines contain three integers separated by dots, of the form
  24  * <tt>{0}.{1}.{2}</tt>:
  25  * * \p {0}: \c PCMK_XA_ADMIN_EPOCH
  26  * * \p {1}: \c PCMK_XA_EPOCH
  27  * * \p {2}: \c PCMK_XA_NUM_UPDATES
  28  *
  29  * Lines containing \p "---" describe removals and end with the patch format
  30  * number. Lines containing \p "+++" describe additions and end with the patch
  31  * digest.
  32  *
  33  * \param[in,out] out       Output object
  34  * \param[in]     patchset  XML patchset to output
  35  *
  36  * \return Standard Pacemaker return code
  37  *
  38  * \note This function produces output only for text-like formats.
  39  */
  40 static int
  41 xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  65 /*!
  66  * \internal
  67  * \brief Output a user-friendly form of an XML patchset
  68  *
  69  * This function parses an XML patchset (a \c PCMK_XE_DIFF element and its
  70  * children) into a user-friendly combined diff output.
  71  *
  72  * \param[in,out] out       Output object
  73  * \param[in]     patchset  XML patchset to output
  74  *
  75  * \return Standard Pacemaker return code
  76  *
  77  * \note This function produces output only for text-like formats.
  78  */
  79 static int
  80 xml_show_patchset(pcmk__output_t *out, const xmlNode *patchset)
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
 101                                      pcmk__xml_fmt_pretty|pcmk__xml_fmt_open);
 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,
 110                                      pcmk__xml_fmt_pretty
 111                                      |pcmk__xml_fmt_children
 112                                      |pcmk__xml_fmt_close);
 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 
 136                 op = crm_element_value(child, PCMK_XA_OPERATION);
 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 
 182 /*!
 183  * \internal
 184  * \brief Output a user-friendly form of an XML patchset
 185  *
 186  * This function parses an XML patchset (a \c PCMK_XE_DIFF element and its
 187  * children) into a user-friendly combined diff output.
 188  *
 189  * \param[in,out] out   Output object
 190  * \param[in]     args  Message-specific arguments
 191  *
 192  * \return Standard Pacemaker return code
 193  *
 194  * \note \p args should contain the following:
 195  *       -# XML patchset
 196  */
 197 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 219 /*!
 220  * \internal
 221  * \brief Output a user-friendly form of an XML patchset
 222  *
 223  * This function parses an XML patchset (a \c PCMK_XE_DIFF element and its
 224  * children) into a user-friendly combined diff output.
 225  *
 226  * \param[in,out] out   Output object
 227  * \param[in]     args  Message-specific arguments
 228  *
 229  * \return Standard Pacemaker return code
 230  *
 231  * \note \p args should contain the following:
 232  *       -# XML patchset
 233  */
 234 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
 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__,
 257                                           crm_trace_nonlog);
 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 
 274 /*!
 275  * \internal
 276  * \brief Output an XML patchset
 277  *
 278  * This function outputs an XML patchset (a \c PCMK_XE_DIFF element and its
 279  * children) without modification, as a CDATA block.
 280  *
 281  * \param[in,out] out   Output object
 282  * \param[in]     args  Message-specific arguments
 283  *
 284  * \return Standard Pacemaker return code
 285  *
 286  * \note \p args should contain the following:
 287  *       -# XML patchset
 288  */
 289 PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 298         pcmk__xml_string(patchset, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, buf,
 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 
 317 /*!
 318  * \internal
 319  * \brief Register the formatting functions for XML patchsets
 320  *
 321  * \param[in,out] out  Output object
 322  */
 323 void
 324 pcmk__register_patchset_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 325     pcmk__register_messages(out, fmt_functions);
 326 }

/* [previous][next][first][last][top][bottom][index][help] */