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