root/tools/cib_shadow.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_shadow_prompt
  2. shadow_setup
  3. shadow_teardown
  4. 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 
  25 #include <sys/param.h>
  26 #include <crm/crm.h>
  27 #include <sys/stat.h>
  28 #include <sys/types.h>
  29 
  30 #include <stdlib.h>
  31 #include <errno.h>
  32 #include <fcntl.h>
  33 #include <crm/msg_xml.h>
  34 #include <crm/common/xml.h>
  35 
  36 #include <crm/common/ipc.h>
  37 
  38 #include <crm/cib.h>
  39 
  40 int exit_code = pcmk_ok;
  41 GMainLoop *mainloop = NULL;
  42 
  43 const char *host = NULL;
  44 void usage(const char *cmd, int exit_status);
  45 
  46 int command_options = cib_sync_call;
  47 const char *cib_action = NULL;
  48 
  49 cib_t *real_cib = NULL;
  50 
  51 static int force_flag = 0;
  52 static int batch_flag = 0;
  53 
  54 static char *
  55 get_shadow_prompt(const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57     return crm_strdup_printf("shadow[%.40s] # ", name);
  58 }
  59 
  60 static void
  61 shadow_setup(char *name, gboolean do_switch)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     const char *prompt = getenv("PS1");
  64     const char *shell = getenv("SHELL");
  65     char *new_prompt = get_shadow_prompt(name);
  66 
  67     printf("Setting up shadow instance\n");
  68 
  69     if (safe_str_eq(new_prompt, prompt)) {
  70         /* nothing to do */
  71         goto done;
  72 
  73     } else if (batch_flag == FALSE && shell != NULL) {
  74         setenv("PS1", new_prompt, 1);
  75         setenv("CIB_shadow", name, 1);
  76         printf("Type Ctrl-D to exit the crm_shadow shell\n");
  77 
  78         if (strstr(shell, "bash")) {
  79             execl(shell, shell, "--norc", "--noprofile", NULL);
  80         } else {
  81             execl(shell, shell, NULL);
  82         }
  83 
  84     } else if (do_switch) {
  85         printf("To switch to the named shadow instance, paste the following into your shell:\n");
  86 
  87     } else {
  88         printf
  89             ("A new shadow instance was created.  To begin using it paste the following into your shell:\n");
  90     }
  91     printf("  CIB_shadow=%s ; export CIB_shadow\n", name);
  92 
  93   done:
  94     free(new_prompt);
  95 }
  96 
  97 static void
  98 shadow_teardown(char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
  99 {
 100     const char *prompt = getenv("PS1");
 101     char *our_prompt = get_shadow_prompt(name);
 102 
 103     if (prompt != NULL && strstr(prompt, our_prompt)) {
 104         printf("Now type Ctrl-D to exit the crm_shadow shell\n");
 105 
 106     } else {
 107         printf
 108             ("Please remember to unset the CIB_shadow variable by pasting the following into your shell:\n");
 109         printf("  unset CIB_shadow\n");
 110     }
 111     free(our_prompt);
 112 }
 113 
 114 /* *INDENT-OFF* */
 115 static struct crm_option long_options[] = {
 116     /* Top-level Options */
 117     {"help",    0, 0, '?', "\t\tThis text"},
 118     {"version", 0, 0, '$', "\t\tVersion information"  },
 119     {"verbose", 0, 0, 'V', "\t\tIncrease debug output"},
 120 
 121     {"-spacer-",        1, 0, '-', "\nQueries:"},
 122     {"which",   no_argument,       NULL, 'w', "\t\tIndicate the active shadow copy"},
 123     {"display", no_argument,       NULL, 'p', "\t\tDisplay the contents of the active shadow copy"},
 124     {"edit",    no_argument,       NULL, 'E', "\t\tEdit the contents of the active shadow copy with your favorite $EDITOR"},
 125     {"diff",    no_argument,       NULL, 'd', "\t\tDisplay the changes in the active shadow copy\n"},
 126     {"file",    no_argument,       NULL, 'F', "\t\tDisplay the location of the active shadow copy file\n"},
 127 
 128     {"-spacer-",        1, 0, '-', "\nCommands:"},
 129     {"create",          required_argument, NULL, 'c', "\tCreate the named shadow copy of the active cluster configuration"},
 130     {"create-empty",    required_argument, NULL, 'e', "Create the named shadow copy with an empty cluster configuration. Optional: --validate-with"},
 131     {"commit",  required_argument, NULL, 'C', "\tUpload the contents of the named shadow copy to the cluster"},
 132     {"delete",  required_argument, NULL, 'D', "\tDelete the contents of the named shadow copy"},
 133     {"reset",   required_argument, NULL, 'r', "\tRecreate the named shadow copy from the active cluster configuration"},
 134     {"switch",  required_argument, NULL, 's', "\t(Advanced) Switch to the named shadow copy"},
 135 
 136     {"-spacer-",        1, 0, '-', "\nAdditional Options:"},
 137     {"force",   no_argument, NULL, 'f', "\t\t(Advanced) Force the action to be performed"},
 138     {"batch",   no_argument, NULL, 'b', "\t\t(Advanced) Don't spawn a new shell" },
 139     {"all",     no_argument, NULL, 'a', "\t\t(Advanced) Upload the entire CIB, including status, with --commit" },
 140     {"validate-with",     required_argument, NULL, 'v', "(Advanced) Create an older configuration version" },
 141 
 142     {"-spacer-",        1, 0, '-', "\nExamples:", pcmk_option_paragraph},
 143     {"-spacer-",        1, 0, '-', "Create a blank shadow configuration:", pcmk_option_paragraph},
 144     {"-spacer-",        1, 0, '-', " crm_shadow --create-empty myShadow", pcmk_option_example},
 145     {"-spacer-",        1, 0, '-', "Create a shadow configuration from the running cluster:", pcmk_option_paragraph},
 146     {"-spacer-",        1, 0, '-', " crm_shadow --create myShadow", pcmk_option_example},
 147     {"-spacer-",        1, 0, '-', "Display the current shadow configuration:", pcmk_option_paragraph},
 148     {"-spacer-",        1, 0, '-', " crm_shadow --display", pcmk_option_example},
 149     {"-spacer-",        1, 0, '-', "Discard the current shadow configuration (named myShadow):", pcmk_option_paragraph},
 150     {"-spacer-",        1, 0, '-', " crm_shadow --delete myShadow", pcmk_option_example},
 151     {"-spacer-",        1, 0, '-', "Upload the current shadow configuration (named myShadow) to the running cluster:", pcmk_option_paragraph},
 152     {"-spacer-",        1, 0, '-', " crm_shadow --commit myShadow", pcmk_option_example},
 153 
 154     {0, 0, 0, 0}
 155 };
 156 /* *INDENT-ON* */
 157 
 158 int
 159 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161     int rc = 0;
 162     int flag;
 163     int argerr = 0;
 164     static int command = '?';
 165     const char *validation = NULL;
 166     char *shadow = NULL;
 167     char *shadow_file = NULL;
 168     gboolean full_upload = FALSE;
 169     gboolean dangerous_cmd = FALSE;
 170     struct stat buf;
 171     int option_index = 0;
 172 
 173     crm_log_cli_init("crm_shadow");
 174     crm_set_options(NULL, "(query|command) [modifiers]", long_options,
 175                     "Perform configuration changes in a sandbox before updating the live cluster."
 176                     "\n\nSets up an environment in which configuration tools (cibadmin, crm_resource, etc) work"
 177                     " offline instead of against a live cluster, allowing changes to be previewed and tested"
 178                     " for side-effects.\n");
 179 
 180     if (argc < 2) {
 181         crm_help('?', EX_USAGE);
 182     }
 183 
 184     while (1) {
 185         flag = crm_get_option(argc, argv, &option_index);
 186         if (flag == -1 || flag == 0)
 187             break;
 188 
 189         switch (flag) {
 190             case 'a':
 191                 full_upload = TRUE;
 192                 break;
 193             case 'd':
 194             case 'E':
 195             case 'p':
 196             case 'w':
 197             case 'F':
 198                 command = flag;
 199                 free(shadow);
 200                 shadow = NULL;
 201                 {
 202                     const char *env = getenv("CIB_shadow");
 203                     if(env) {
 204                         shadow = strdup(env);
 205                     } else {
 206                         fprintf(stderr, "No active shadow configuration defined\n");
 207                         crm_exit(ENOENT);
 208                     }
 209                 }
 210                 break;
 211             case 'v':
 212                 validation = optarg;
 213                 break;
 214             case 'e':
 215             case 'c':
 216             case 's':
 217             case 'r':
 218                 command = flag;
 219                 free(shadow);
 220                 shadow = strdup(optarg);
 221                 break;
 222             case 'C':
 223             case 'D':
 224                 command = flag;
 225                 dangerous_cmd = TRUE;
 226                 free(shadow);
 227                 shadow = strdup(optarg);
 228                 break;
 229             case 'V':
 230                 command_options = command_options | cib_verbose;
 231                 crm_bump_log_level(argc, argv);
 232                 break;
 233             case '$':
 234             case '?':
 235                 crm_help(flag, EX_OK);
 236                 break;
 237             case 'f':
 238                 command_options |= cib_quorum_override;
 239                 force_flag = 1;
 240                 break;
 241             case 'b':
 242                 batch_flag = 1;
 243                 break;
 244             default:
 245                 printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag);
 246                 ++argerr;
 247                 break;
 248         }
 249     }
 250 
 251     if (optind < argc) {
 252         printf("non-option ARGV-elements: ");
 253         while (optind < argc)
 254             printf("%s ", argv[optind++]);
 255         printf("\n");
 256         crm_help('?', EX_USAGE);
 257     }
 258 
 259     if (optind > argc) {
 260         ++argerr;
 261     }
 262 
 263     if (argerr) {
 264         crm_help('?', EX_USAGE);
 265     }
 266 
 267     if (command == 'w') {
 268         /* which shadow instance is active? */
 269         const char *local = getenv("CIB_shadow");
 270 
 271         if (local == NULL) {
 272             fprintf(stderr, "No shadow instance provided\n");
 273             rc = -ENXIO;
 274             goto done;
 275         }
 276         fprintf(stdout, "%s\n", local);
 277         rc = 0;
 278         goto done;
 279     }
 280 
 281     if (shadow == NULL) {
 282         fprintf(stderr, "No shadow instance provided\n");
 283         fflush(stderr);
 284         rc = -EINVAL;
 285         goto done;
 286 
 287     } else if (command != 's' && command != 'c') {
 288         const char *local = getenv("CIB_shadow");
 289 
 290         if (local != NULL && safe_str_neq(local, shadow) && force_flag == FALSE) {
 291             fprintf(stderr,
 292                     "The supplied shadow instance (%s) is not the same as the active one (%s).\n"
 293                     "  To prevent accidental destruction of the cluster,"
 294                     " the --force flag is required in order to proceed.\n", shadow, local);
 295             fflush(stderr);
 296             rc = EX_USAGE;
 297             goto done;
 298         }
 299     }
 300 
 301     if (dangerous_cmd && force_flag == FALSE) {
 302         fprintf(stderr, "The supplied command is considered dangerous."
 303                 "  To prevent accidental destruction of the cluster,"
 304                 " the --force flag is required in order to proceed.\n");
 305         fflush(stderr);
 306         rc = EX_USAGE;
 307         goto done;
 308     }
 309 
 310     shadow_file = get_shadow_file(shadow);
 311     if (command == 'D') {
 312         /* delete the file */
 313         rc = stat(shadow_file, &buf);
 314         if (rc == 0) {
 315             rc = unlink(shadow_file);
 316             if (rc != 0) {
 317                 fprintf(stderr, "Could not remove shadow instance '%s': %s\n", shadow,
 318                         strerror(errno));
 319                 goto done;
 320             }
 321         }
 322 
 323         shadow_teardown(shadow);
 324         goto done;
 325 
 326     } else if (command == 'F') {
 327         printf("%s\n", shadow_file);
 328         rc = 0;
 329         goto done;
 330     }
 331 
 332     if (command == 'd' || command == 'r' || command == 'c' || command == 'C') {
 333         real_cib = cib_new_no_shadow();
 334         rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command);
 335         if (rc != pcmk_ok) {
 336             fprintf(stderr, "Signon to CIB failed: %s\n", pcmk_strerror(rc));
 337             goto done;
 338         }
 339     }
 340 
 341     rc = stat(shadow_file, &buf);
 342 
 343     if (command == 'e' || command == 'c') {
 344         if (rc == 0 && force_flag == FALSE) {
 345             fprintf(stderr, "A shadow instance '%s' already exists.\n"
 346                     "  To prevent accidental destruction of the cluster,"
 347                     " the --force flag is required in order to proceed.\n", shadow);
 348             rc = -ENOTUNIQ;
 349             goto done;
 350         }
 351 
 352     } else if (rc != 0) {
 353         fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno));
 354         rc = -ENXIO;
 355         goto done;
 356     }
 357 
 358     rc = pcmk_ok;
 359     if (command == 'c' || command == 'e' || command == 'r') {
 360         xmlNode *output = NULL;
 361 
 362         /* create a shadow instance based on the current cluster config */
 363         if (command == 'c' || command == 'r') {
 364             rc = real_cib->cmds->query(real_cib, NULL, &output, command_options);
 365             if (rc != pcmk_ok) {
 366                 fprintf(stderr, "Could not connect to the CIB: %s\n", pcmk_strerror(rc));
 367                 goto done;
 368             }
 369 
 370         } else {
 371             output = createEmptyCib(0);
 372             if(validation) {
 373                 crm_xml_add(output, XML_ATTR_VALIDATION, validation);
 374             }
 375             printf("Created new %s configuration\n",
 376                    crm_element_value(output, XML_ATTR_VALIDATION));
 377         }
 378 
 379         rc = write_xml_file(output, shadow_file, FALSE);
 380         free_xml(output);
 381 
 382         if (rc < 0) {
 383             fprintf(stderr, "Could not %s the shadow instance '%s': %s\n",
 384                     command == 'r' ? "reset" : "create",
 385                     shadow, strerror(errno));
 386             goto done;
 387         }
 388         shadow_setup(shadow, FALSE);
 389         rc = pcmk_ok;
 390 
 391     } else if (command == 'E') {
 392         const char *err = NULL;
 393         char *editor = getenv("EDITOR");
 394 
 395         if (editor == NULL) {
 396             fprintf(stderr, "No value for $EDITOR defined\n");
 397             rc = -EINVAL;
 398             goto done;
 399         }
 400 
 401         execlp(editor, "--", shadow_file, NULL);
 402         err = strerror(errno);
 403         fprintf(stderr, "Could not invoke $EDITOR (%s %s): %s\n", editor, shadow_file, err);
 404         rc = -EINVAL;
 405         goto done;
 406 
 407     } else if (command == 's') {
 408         shadow_setup(shadow, TRUE);
 409         rc = 0;
 410         goto done;
 411 
 412     } else if (command == 'p') {
 413         /* display the current contents */
 414         char *output_s = NULL;
 415         xmlNode *output = filename2xml(shadow_file);
 416 
 417         output_s = dump_xml_formatted(output);
 418         printf("%s", output_s);
 419 
 420         free(output_s);
 421         free_xml(output);
 422 
 423     } else if (command == 'd') {
 424         /* diff against cluster */
 425         xmlNode *diff = NULL;
 426         xmlNode *old_config = NULL;
 427         xmlNode *new_config = filename2xml(shadow_file);
 428 
 429         rc = real_cib->cmds->query(real_cib, NULL, &old_config, command_options);
 430 
 431         if (rc != pcmk_ok) {
 432             fprintf(stderr, "Could not query the CIB: %s\n", pcmk_strerror(rc));
 433             goto done;
 434         }
 435 
 436         xml_track_changes(new_config, NULL, new_config, FALSE);
 437         xml_calculate_changes(old_config, new_config);
 438 
 439         diff = xml_create_patchset(0, old_config, new_config, NULL, FALSE);
 440 
 441         xml_log_changes(LOG_INFO, __FUNCTION__, new_config);
 442         xml_accept_changes(new_config);
 443 
 444         if (diff != NULL) {
 445             xml_log_patchset(0, "  ", diff);
 446             rc = 1;
 447             goto done;
 448         }
 449         rc = 0;
 450         goto done;
 451 
 452     } else if (command == 'C') {
 453         /* commit to the cluster */
 454         xmlNode *input = filename2xml(shadow_file);
 455 
 456         if (full_upload) {
 457             rc = real_cib->cmds->replace(real_cib, NULL, input, command_options);
 458         } else {
 459             xmlNode *config = first_named_child(input, XML_CIB_TAG_CONFIGURATION);
 460 
 461             rc = real_cib->cmds->replace(real_cib, XML_CIB_TAG_CONFIGURATION, config,
 462                                          command_options);
 463         }
 464 
 465         if (rc != pcmk_ok) {
 466             fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n",
 467                     shadow, pcmk_strerror(rc));
 468             goto done;
 469         }
 470         shadow_teardown(shadow);
 471         free_xml(input);
 472     }
 473   done:
 474     free(shadow_file);
 475     free(shadow);
 476     return crm_exit(rc);
 477 }

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