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 free(shadow);
309 shadow = strdup(optarg);
310 break;
311 case 'C':
312 case 'D':
313 command = flag;
314 dangerous_cmd = TRUE;
315 free(shadow);
316 shadow = strdup(optarg);
317 break;
318 case 'V':
319 command_options = command_options | cib_verbose;
320 crm_bump_log_level(argc, argv);
321 break;
322 case '$':
323 case '?':
324 pcmk__cli_help(flag, CRM_EX_OK);
325 break;
326 case 'f':
327 cib__set_call_options(command_options, crm_system_name,
328 cib_quorum_override);
329 force_flag = 1;
330 break;
331 case 'b':
332 batch_flag = 1;
333 break;
334 default:
335 printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag);
336 ++argerr;
337 break;
338 }
339 }
340
341 if (optind < argc) {
342 printf("non-option ARGV-elements: ");
343 while (optind < argc)
344 printf("%s ", argv[optind++]);
345 printf("\n");
346 pcmk__cli_help('?', CRM_EX_USAGE);
347 }
348
349 if (optind > argc) {
350 ++argerr;
351 }
352
353 if (argerr) {
354 pcmk__cli_help('?', CRM_EX_USAGE);
355 }
356
357 if (command == 'w') {
358
359 const char *local = getenv("CIB_shadow");
360
361 if (local == NULL) {
362 fprintf(stderr, "No shadow instance provided\n");
363 exit_code = CRM_EX_NOSUCH;
364 } else {
365 fprintf(stdout, "%s\n", local);
366 }
367 goto done;
368 }
369
370 if (shadow == NULL) {
371 fprintf(stderr, "No shadow instance provided\n");
372 fflush(stderr);
373 exit_code = CRM_EX_NOSUCH;
374 goto done;
375
376 } else if (command != 's' && command != 'c') {
377 const char *local = getenv("CIB_shadow");
378
379 if (local != NULL && !pcmk__str_eq(local, shadow, pcmk__str_casei) && force_flag == FALSE) {
380 fprintf(stderr,
381 "The supplied shadow instance (%s) is not the same as the active one (%s).\n"
382 " To prevent accidental destruction of the cluster,"
383 " the --force flag is required in order to proceed.\n", shadow, local);
384 fflush(stderr);
385 exit_code = CRM_EX_USAGE;
386 goto done;
387 }
388 }
389
390 if (dangerous_cmd && force_flag == FALSE) {
391 fprintf(stderr, "The supplied command is considered dangerous."
392 " To prevent accidental destruction of the cluster,"
393 " the --force flag is required in order to proceed.\n");
394 fflush(stderr);
395 exit_code = CRM_EX_USAGE;
396 goto done;
397 }
398
399 shadow_file = get_shadow_file(shadow);
400 if (command == 'D') {
401
402 if ((unlink(shadow_file) < 0) && (errno != ENOENT)) {
403 exit_code = crm_errno2exit(errno);
404 fprintf(stderr, "Could not remove shadow instance '%s': %s\n",
405 shadow, strerror(errno));
406 }
407 shadow_teardown(shadow);
408 goto done;
409
410 } else if (command == 'F') {
411 printf("%s\n", shadow_file);
412 goto done;
413 }
414
415 if (command == 'd' || command == 'r' || command == 'c' || command == 'C') {
416 real_cib = cib_new_no_shadow();
417 rc = real_cib->cmds->signon(real_cib, crm_system_name, cib_command);
418 if (rc != pcmk_ok) {
419 fprintf(stderr, "Could not connect to CIB: %s\n",
420 pcmk_strerror(rc));
421 exit_code = crm_errno2exit(rc);
422 goto done;
423 }
424 }
425
426
427 rc = stat(shadow_file, &buf);
428 if (command == 'e' || command == 'c') {
429 if (rc == 0 && force_flag == FALSE) {
430 fprintf(stderr, "A shadow instance '%s' already exists.\n"
431 " To prevent accidental destruction of the cluster,"
432 " the --force flag is required in order to proceed.\n", shadow);
433 exit_code = CRM_EX_CANTCREAT;
434 goto done;
435 }
436 } else if (rc < 0) {
437 fprintf(stderr, "Could not access shadow instance '%s': %s\n", shadow, strerror(errno));
438 exit_code = CRM_EX_NOSUCH;
439 goto done;
440 }
441
442 if (command == 'c' || command == 'e' || command == 'r') {
443 xmlNode *output = NULL;
444
445
446 if (command == 'c' || command == 'r') {
447 rc = real_cib->cmds->query(real_cib, NULL, &output, command_options);
448 if (rc != pcmk_ok) {
449 fprintf(stderr, "Could not connect to the CIB manager: %s\n",
450 pcmk_strerror(rc));
451 exit_code = crm_errno2exit(rc);
452 goto done;
453 }
454
455 } else {
456 output = createEmptyCib(0);
457 if(validation) {
458 crm_xml_add(output, XML_ATTR_VALIDATION, validation);
459 }
460 printf("Created new %s configuration\n",
461 crm_element_value(output, XML_ATTR_VALIDATION));
462 }
463
464 rc = write_xml_file(output, shadow_file, FALSE);
465 free_xml(output);
466
467 if (rc < 0) {
468 fprintf(stderr, "Could not %s the shadow instance '%s': %s\n",
469 command == 'r' ? "reset" : "create",
470 shadow, pcmk_strerror(rc));
471 exit_code = crm_errno2exit(rc);
472 goto done;
473 }
474 shadow_setup(shadow, FALSE);
475
476 } else if (command == 'E') {
477 char *editor = getenv("EDITOR");
478
479 if (editor == NULL) {
480 fprintf(stderr, "No value for EDITOR defined\n");
481 exit_code = CRM_EX_NOT_CONFIGURED;
482 goto done;
483 }
484
485 execlp(editor, "--", shadow_file, NULL);
486 fprintf(stderr, "Could not invoke EDITOR (%s %s): %s\n",
487 editor, shadow_file, strerror(errno));
488 exit_code = CRM_EX_OSFILE;
489 goto done;
490
491 } else if (command == 's') {
492 shadow_setup(shadow, TRUE);
493 goto done;
494
495 } else if (command == 'p') {
496
497 char *output_s = NULL;
498 xmlNode *output = filename2xml(shadow_file);
499
500 output_s = dump_xml_formatted(output);
501 printf("%s", output_s);
502
503 free(output_s);
504 free_xml(output);
505
506 } else if (command == 'd') {
507
508 xmlNode *diff = NULL;
509 xmlNode *old_config = NULL;
510 xmlNode *new_config = filename2xml(shadow_file);
511
512 rc = real_cib->cmds->query(real_cib, NULL, &old_config, command_options);
513
514 if (rc != pcmk_ok) {
515 fprintf(stderr, "Could not query the CIB: %s\n", pcmk_strerror(rc));
516 exit_code = crm_errno2exit(rc);
517 goto done;
518 }
519
520 xml_track_changes(new_config, NULL, new_config, FALSE);
521 xml_calculate_changes(old_config, new_config);
522
523 diff = xml_create_patchset(0, old_config, new_config, NULL, FALSE);
524
525 xml_log_changes(LOG_INFO, __func__, new_config);
526 xml_accept_changes(new_config);
527
528 if (diff != NULL) {
529 xml_log_patchset(LOG_STDOUT, " ", diff);
530 exit_code = CRM_EX_ERROR;
531 }
532 goto done;
533
534 } else if (command == 'C') {
535
536 xmlNode *input = filename2xml(shadow_file);
537 xmlNode *section_xml = input;
538 const char *section = NULL;
539
540 if (!full_upload) {
541 section = XML_CIB_TAG_CONFIGURATION;
542 section_xml = first_named_child(input, section);
543 }
544 rc = real_cib->cmds->replace(real_cib, section, section_xml,
545 command_options);
546 if (rc != pcmk_ok) {
547 fprintf(stderr, "Could not commit shadow instance '%s' to the CIB: %s\n",
548 shadow, pcmk_strerror(rc));
549 exit_code = crm_errno2exit(rc);
550 }
551 shadow_teardown(shadow);
552 free_xml(input);
553 }
554 done:
555 free(shadow_file);
556 free(shadow);
557 crm_exit(exit_code);
558 }