root/tools/crm_diff.c

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

DEFINITIONS

This source file includes following definitions.
  1. print_patch
  2. apply_patch
  3. log_patch_cib_versions
  4. strip_patch_cib_version
  5. generate_patch
  6. main

   1 
   2 /* 
   3  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   4  * 
   5  * This program is free software; you can redistribute it and/or
   6  * modify it under the terms of the GNU General Public
   7  * License as published by the Free Software Foundation; either
   8  * version 2 of the License, or (at your option) any later version.
   9  * 
  10  * This software is distributed in the hope that it will be useful,
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13  * General Public License for more details.
  14  * 
  15  * You should have received a copy of the GNU General Public
  16  * License along with this library; if not, write to the Free Software
  17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  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 /* *INDENT-OFF* */
  37 static struct crm_option long_options[] = {
  38     /* Top-level Options */
  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 /* *INDENT-ON* */
  70 
  71 static void
  72 print_patch(xmlNode *patch)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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     /* If we're ignoring the version, make the version information
 183      * identical, so it isn't detected as a change. */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 }

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