This source file includes following definitions.
- print_xml_output
- main
- do_work
- do_init
- cib_connection_destroy
- cibadmin_op_callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <crm_internal.h>
21
22 #include <sys/param.h>
23
24 #include <crm/crm.h>
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <crm/msg_xml.h>
35 #include <crm/common/xml.h>
36 #include <crm/common/ipc.h>
37 #include <crm/cib/internal.h>
38
39 int exit_code = pcmk_ok;
40 int message_timer_id = -1;
41 int message_timeout_ms = 30;
42
43 GMainLoop *mainloop = NULL;
44
45 const char *host = NULL;
46 void usage(const char *cmd, int exit_status);
47 int do_init(void);
48 int do_work(xmlNode * input, int command_options, xmlNode ** output);
49
50 gboolean admin_message_timeout(gpointer data);
51 void cib_connection_destroy(gpointer user_data);
52 void cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data);
53
54 int command_options = 0;
55 const char *cib_user = NULL;
56 const char *cib_action = NULL;
57
58 typedef struct str_list_s {
59 int num_items;
60 char *value;
61 struct str_list_s *next;
62 } str_list_t;
63
64 const char *obj_type = NULL;
65 char *status = NULL;
66 char *migrate_from = NULL;
67 char *migrate_res = NULL;
68 char *subtype = NULL;
69 char *reset = NULL;
70
71 int request_id = 0;
72 int operation_status = 0;
73 cib_t *the_cib = NULL;
74 gboolean force_flag = FALSE;
75 gboolean quiet = FALSE;
76 int bump_log_num = 0;
77
78
79 static struct crm_option long_options[] = {
80 {"help", 0, 0, '?', "\tThis text"},
81 {"version", 0, 0, '$', "\tVersion information" },
82 {"verbose", 0, 0, 'V', "\tIncrease debug output\n"},
83
84 {"-spacer-", 0, 0, '-', "Commands:"},
85 {"upgrade", 0, 0, 'u', "\tUpgrade the configuration to the latest syntax"},
86 {"query", 0, 0, 'Q', "\tQuery the contents of the CIB"},
87 {"erase", 0, 0, 'E', "\tErase the contents of the whole CIB"},
88 {"bump", 0, 0, 'B', "\tIncrease the CIB's epoch value by 1"},
89 {"create", 0, 0, 'C', "\tCreate an object in the CIB. Will fail if the object already exists."},
90 {"modify", 0, 0, 'M', "\tFind the object somewhere in the CIB's XML tree and update it. Fails if the object does not exist unless -c is specified"},
91 {"patch", 0, 0, 'P', "\tSupply an update in the form of an xml diff (See also: crm_diff)"},
92 {"replace", 0, 0, 'R', "\tRecursively replace an object in the CIB"},
93 {"delete", 0, 0, 'D', "\tDelete the first object matching the supplied criteria, Eg. <op id=\"rsc1_op1\" name=\"monitor\"/>"},
94 {"-spacer-", 0, 0, '-', "\n\tThe tagname and all attributes must match in order for the element to be deleted\n"},
95 {"delete-all", 0, 0, 'd', "When used with --xpath, remove all matching objects in the configuration instead of just the first one"},
96 {"empty", 0, 0, 'a', "\tOutput an empty CIB"},
97 {"md5-sum", 0, 0, '5', "\tCalculate the on-disk CIB digest"},
98 {"md5-sum-versioned", 0, 0, '6', "Calculate an on-the-wire versioned CIB digest"},
99 {"blank", 0, 0, '-', NULL, 1},
100
101 {"-spacer-",1, 0, '-', "\nAdditional options:"},
102 {"force", 0, 0, 'f'},
103 {"timeout", 1, 0, 't', "Time (in seconds) to wait before declaring the operation failed"},
104 {"user", 1, 0, 'U', "Run the command with permissions of the named user (valid only for the root and "CRM_DAEMON_USER" accounts)"},
105 {"sync-call", 0, 0, 's', "Wait for call to complete before returning"},
106 {"local", 0, 0, 'l', "\tCommand takes effect locally. Should only be used for queries"},
107 {"allow-create",0, 0, 'c', "(Advanced) Allow the target of a --modify,-M operation to be created if they do not exist"},
108 {"no-children", 0, 0, 'n', "(Advanced) When querying an object, do not return include its children in the result\n"},
109 {"no-bcast", 0, 0, 'b', NULL, 1},
110
111 {"-spacer-", 0, 0, '-', "Data:"},
112 {"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"},
113 {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"},
114 {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"},
115
116 {"scope", 1, 0, 'o', "Limit the scope of the operation to a specific section of the CIB."},
117 {"-spacer-", 0, 0, '-', "\tValid values are: nodes, resources, constraints, crm_config, rsc_defaults, op_defaults, status"},
118
119 {"xpath", 1, 0, 'A', "A valid XPath to use instead of --scope,-o"},
120 {"node-path", 0, 0, 'e', "When performing XPath queries, return the address of any matches found."},
121 {"-spacer-", 0, 0, '-', " Eg: /cib/configuration/resources/master[@id='ms_RH1_SCS']/primitive[@id='prm_RH1_SCS']", pcmk_option_paragraph},
122 {"node", 1, 0, 'N', "(Advanced) Send command to the specified host\n"},
123 {"-space-", 0, 0, '!', NULL, 1},
124
125 {"-spacer-", 0, 0, '-', "\nExamples:\n"},
126 {"-spacer-", 0, 0, '-', "Query the configuration from the local node:", pcmk_option_paragraph},
127 {"-spacer-", 0, 0, '-', " cibadmin --query --local", pcmk_option_example},
128
129 {"-spacer-", 0, 0, '-', "Query just the cluster options configuration:", pcmk_option_paragraph},
130 {"-spacer-", 0, 0, '-', " cibadmin --query --scope crm_config", pcmk_option_example},
131
132 {"-spacer-", 0, 0, '-', "Query all 'target-role' settings:", pcmk_option_paragraph},
133 {"-spacer-", 0, 0, '-', " cibadmin --query --xpath \"//nvpair[@name='target-role']\"", pcmk_option_example},
134
135 {"-spacer-", 0, 0, '-', "Remove all 'is-managed' settings:", pcmk_option_paragraph},
136 {"-spacer-", 0, 0, '-', " cibadmin --delete-all --xpath \"//nvpair[@name='is-managed']\"", pcmk_option_example},
137
138 {"-spacer-", 0, 0, '-', "Remove the resource named 'old':", pcmk_option_paragraph},
139 {"-spacer-", 0, 0, '-', " cibadmin --delete --xml-text '<primitive id=\"old\"/>'", pcmk_option_example},
140
141 {"-spacer-", 0, 0, '-', "Remove all resources from the configuration:", pcmk_option_paragraph},
142 {"-spacer-", 0, 0, '-', " cibadmin --replace --scope resources --xml-text '<resources/>'", pcmk_option_example},
143
144 {"-spacer-", 0, 0, '-', "Replace the complete configuration with the contents of $HOME/pacemaker.xml:", pcmk_option_paragraph},
145 {"-spacer-", 0, 0, '-', " cibadmin --replace --xml-file $HOME/pacemaker.xml", pcmk_option_example},
146
147 {"-spacer-", 0, 0, '-', "Replace the constraints section of the configuration with the contents of $HOME/constraints.xml:", pcmk_option_paragraph},
148 {"-spacer-", 0, 0, '-', " cibadmin --replace --scope constraints --xml-file $HOME/constraints.xml", pcmk_option_example},
149
150 {"-spacer-", 0, 0, '-', "Increase the configuration version to prevent old configurations from being loaded accidentally:", pcmk_option_paragraph},
151 {"-spacer-", 0, 0, '-', " cibadmin --modify --xml-text '<cib admin_epoch=\"admin_epoch++\"/>'", pcmk_option_example},
152
153 {"-spacer-", 0, 0, '-', "Edit the configuration with your favorite $EDITOR:", pcmk_option_paragraph},
154 {"-spacer-", 0, 0, '-', " cibadmin --query > $HOME/local.xml", pcmk_option_example},
155 {"-spacer-", 0, 0, '-', " $EDITOR $HOME/local.xml", pcmk_option_example},
156 {"-spacer-", 0, 0, '-', " cibadmin --replace --xml-file $HOME/local.xml", pcmk_option_example},
157
158 {"-spacer-", 0, 0, '-', "SEE ALSO:"},
159 {"-spacer-", 0, 0, '-', " crm(8), pcs(8), crm_shadow(8), crm_diff(8)"},
160
161
162 {"host", 1, 0, 'h', NULL, 1},
163
164 {0, 0, 0, 0}
165 };
166
167
168 static void
169 print_xml_output(xmlNode * xml)
170 {
171 char *buffer;
172
173 if (!xml) {
174 return;
175 } else if (xml->type != XML_ELEMENT_NODE) {
176 return;
177 }
178
179 if (command_options & cib_xpath_address) {
180 const char *id = crm_element_value(xml, XML_ATTR_ID);
181
182 if (safe_str_eq((const char *)xml->name, "xpath-query")) {
183 xmlNode *child = NULL;
184
185 for (child = xml->children; child; child = child->next) {
186 print_xml_output(child);
187 }
188
189 } else if (id) {
190 printf("%s\n", id);
191 }
192
193 } else {
194 buffer = dump_xml_formatted(xml);
195 fprintf(stdout, "%s", crm_str(buffer));
196 free(buffer);
197 }
198 }
199
200 int
201 main(int argc, char **argv)
202 {
203 int argerr = 0;
204 int flag;
205 const char *source = NULL;
206 const char *admin_input_xml = NULL;
207 const char *admin_input_file = NULL;
208 gboolean dangerous_cmd = FALSE;
209 gboolean admin_input_stdin = FALSE;
210 xmlNode *output = NULL;
211 xmlNode *input = NULL;
212
213 int option_index = 0;
214
215 crm_xml_init();
216 crm_log_cli_init("cibadmin");
217 set_crm_log_level(LOG_CRIT);
218 crm_set_options(NULL, "command [options] [data]", long_options,
219 "Provides direct access to the cluster configuration."
220 "\n\nAllows the configuration, or sections of it, to be queried, modified, replaced and deleted."
221 "\n\nWhere necessary, XML data will be obtained using the -X, -x, or -p options.\n");
222
223 if (argc < 2) {
224 crm_help('?', EX_USAGE);
225 }
226
227 while (1) {
228 flag = crm_get_option(argc, argv, &option_index);
229 if (flag == -1)
230 break;
231
232 switch (flag) {
233 case 't':
234 message_timeout_ms = atoi(optarg);
235 if (message_timeout_ms < 1) {
236 message_timeout_ms = 30;
237 }
238 break;
239 case 'A':
240 obj_type = optarg;
241 command_options |= cib_xpath;
242 break;
243 case 'e':
244 command_options |= cib_xpath_address;
245 break;
246 case 'u':
247 cib_action = CIB_OP_UPGRADE;
248 dangerous_cmd = TRUE;
249 break;
250 case 'E':
251 cib_action = CIB_OP_ERASE;
252 dangerous_cmd = TRUE;
253 break;
254 case 'Q':
255 cib_action = CIB_OP_QUERY;
256 quiet = TRUE;
257 break;
258 case 'P':
259 cib_action = CIB_OP_APPLY_DIFF;
260 break;
261 case 'U':
262 cib_user = optarg;
263 break;
264 case 'M':
265 cib_action = CIB_OP_MODIFY;
266 break;
267 case 'R':
268 cib_action = CIB_OP_REPLACE;
269 break;
270 case 'C':
271 cib_action = CIB_OP_CREATE;
272 break;
273 case 'D':
274 cib_action = CIB_OP_DELETE;
275 break;
276 case '5':
277 cib_action = "md5-sum";
278 break;
279 case '6':
280 cib_action = "md5-sum-versioned";
281 break;
282 case 'c':
283 command_options |= cib_can_create;
284 break;
285 case 'n':
286 command_options |= cib_no_children;
287 break;
288 case 'B':
289 cib_action = CIB_OP_BUMP;
290 crm_log_args(argc, argv);
291 break;
292 case 'V':
293 command_options = command_options | cib_verbose;
294 bump_log_num++;
295 break;
296 case '?':
297 case '$':
298 case '!':
299 crm_help(flag, EX_OK);
300 break;
301 case 'o':
302 crm_trace("Option %c => %s", flag, optarg);
303 obj_type = optarg;
304 break;
305 case 'X':
306 crm_trace("Option %c => %s", flag, optarg);
307 admin_input_xml = optarg;
308 crm_log_args(argc, argv);
309 break;
310 case 'x':
311 crm_trace("Option %c => %s", flag, optarg);
312 admin_input_file = optarg;
313 crm_log_args(argc, argv);
314 break;
315 case 'p':
316 admin_input_stdin = TRUE;
317 crm_log_args(argc, argv);
318 break;
319 case 'N':
320 case 'h':
321 host = strdup(optarg);
322 break;
323 case 'l':
324 command_options |= cib_scope_local;
325 break;
326 case 'd':
327 cib_action = CIB_OP_DELETE;
328 command_options |= cib_multiple;
329 dangerous_cmd = TRUE;
330 break;
331 case 'b':
332 dangerous_cmd = TRUE;
333 command_options |= cib_inhibit_bcast;
334 command_options |= cib_scope_local;
335 break;
336 case 's':
337 command_options |= cib_sync_call;
338 break;
339 case 'f':
340 force_flag = TRUE;
341 command_options |= cib_quorum_override;
342 crm_log_args(argc, argv);
343 break;
344 case 'a':
345 output = createEmptyCib(1);
346 if (optind < argc) {
347 crm_xml_add(output, XML_ATTR_VALIDATION, argv[optind]);
348 }
349 admin_input_xml = dump_xml_formatted(output);
350 fprintf(stdout, "%s\n", crm_str(admin_input_xml));
351 goto bail;
352 break;
353 default:
354 printf("Argument code 0%o (%c)" " is not (?yet?) supported\n", flag, flag);
355 ++argerr;
356 break;
357 }
358 }
359
360 if (bump_log_num > 0) {
361 quiet = FALSE;
362 }
363
364 while (bump_log_num > 0) {
365 crm_bump_log_level(argc, argv);
366 bump_log_num--;
367 }
368
369 if (optind < argc) {
370 printf("non-option ARGV-elements: ");
371 while (optind < argc)
372 printf("%s ", argv[optind++]);
373 printf("\n");
374 crm_help('?', EX_USAGE);
375 }
376
377 if (optind > argc || cib_action == NULL) {
378 ++argerr;
379 }
380
381 if (argerr) {
382 crm_help('?', EX_USAGE);
383 }
384
385 if (dangerous_cmd && force_flag == FALSE) {
386 fprintf(stderr, "The supplied command is considered dangerous."
387 " To prevent accidental destruction of the cluster,"
388 " the --force flag is required in order to proceed.\n");
389 fflush(stderr);
390 exit_code = -EINVAL;
391 goto bail;
392 }
393
394 if (admin_input_file != NULL) {
395 input = filename2xml(admin_input_file);
396 source = admin_input_file;
397
398 } else if (admin_input_xml != NULL) {
399 source = "input string";
400 input = string2xml(admin_input_xml);
401
402 } else if (admin_input_stdin) {
403 source = "STDIN";
404 input = stdin2xml();
405 }
406
407 if (input != NULL) {
408 crm_log_xml_debug(input, "[admin input]");
409
410 } else if (source) {
411 fprintf(stderr, "Couldn't parse input from %s.\n", source);
412 exit_code = -EINVAL;
413 goto bail;
414 }
415
416 if (safe_str_eq(cib_action, "md5-sum")) {
417 char *digest = NULL;
418
419 if (input == NULL) {
420 fprintf(stderr, "Please supply XML to process with -X, -x or -p\n");
421 exit_code = -EINVAL;
422 goto bail;
423 }
424
425 digest = calculate_on_disk_digest(input);
426 fprintf(stderr, "Digest: ");
427 fprintf(stdout, "%s\n", crm_str(digest));
428 free(digest);
429 goto bail;
430
431 } else if (safe_str_eq(cib_action, "md5-sum-versioned")) {
432 char *digest = NULL;
433 const char *version = NULL;
434
435 if (input == NULL) {
436 fprintf(stderr, "Please supply XML to process with -X, -x or -p\n");
437 exit_code = -EINVAL;
438 goto bail;
439 }
440
441 version = crm_element_value(input, XML_ATTR_CRM_VERSION);
442 digest = calculate_xml_versioned_digest(input, FALSE, TRUE, version);
443 fprintf(stderr, "Versioned (%s) digest: ", version);
444 fprintf(stdout, "%s\n", crm_str(digest));
445 free(digest);
446 goto bail;
447 }
448
449 exit_code = do_init();
450 if (exit_code != pcmk_ok) {
451 crm_err("Init failed, could not perform requested operations");
452 fprintf(stderr, "Init failed, could not perform requested operations\n");
453 return crm_exit(-exit_code);
454 }
455
456 exit_code = do_work(input, command_options, &output);
457 if (exit_code > 0) {
458
459
460
461 request_id = exit_code;
462
463 the_cib->cmds->register_callback(the_cib, request_id, message_timeout_ms, FALSE, NULL,
464 "cibadmin_op_callback", cibadmin_op_callback);
465
466 mainloop = g_main_new(FALSE);
467
468 crm_trace("%s waiting for reply from the local CIB", crm_system_name);
469
470 crm_info("Starting mainloop");
471 g_main_run(mainloop);
472
473 } else if (exit_code < 0) {
474 crm_err("Call failed: %s", pcmk_strerror(exit_code));
475 fprintf(stderr, "Call failed: %s\n", pcmk_strerror(exit_code));
476 operation_status = exit_code;
477
478 if (exit_code == -pcmk_err_schema_validation) {
479 if (crm_str_eq(cib_action, CIB_OP_UPGRADE, TRUE)) {
480 xmlNode *obj = NULL;
481 int version = 0, rc = 0;
482
483 rc = the_cib->cmds->query(the_cib, NULL, &obj, command_options);
484 if (rc == pcmk_ok) {
485 update_validation(&obj, &version, 0, TRUE, FALSE);
486 }
487
488 } else if (output) {
489 validate_xml_verbose(output);
490 }
491 }
492 }
493
494 if (output != NULL) {
495 print_xml_output(output);
496 free_xml(output);
497 }
498
499 crm_trace("%s exiting normally", crm_system_name);
500
501 free_xml(input);
502 flag = the_cib->cmds->signoff(the_cib);
503 cib_delete(the_cib);
504
505 if(exit_code == pcmk_ok) {
506 exit_code = flag;
507 }
508 bail:
509 return crm_exit(exit_code);
510 }
511
512 int
513 do_work(xmlNode * input, int call_options, xmlNode ** output)
514 {
515
516 the_cib->call_timeout = message_timeout_ms;
517 if (strcasecmp(CIB_OP_REPLACE, cib_action) == 0
518 && safe_str_eq(crm_element_name(input), XML_TAG_CIB)) {
519 xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, input);
520
521 if (status == NULL) {
522 create_xml_node(input, XML_CIB_TAG_STATUS);
523 }
524 }
525
526 if (cib_action != NULL) {
527 crm_trace("Passing \"%s\" to variant_op...", cib_action);
528 return cib_internal_op(the_cib, cib_action, host, obj_type, input, output, call_options, cib_user);
529
530 } else {
531 crm_err("You must specify an operation");
532 }
533 return -EINVAL;
534 }
535
536 int
537 do_init(void)
538 {
539 int rc = pcmk_ok;
540
541 the_cib = cib_new();
542 rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command);
543 if (rc != pcmk_ok) {
544 crm_err("Signon to CIB failed: %s", pcmk_strerror(rc));
545 fprintf(stderr, "Signon to CIB failed: %s\n", pcmk_strerror(rc));
546 }
547
548 return rc;
549 }
550
551 void
552 cib_connection_destroy(gpointer user_data)
553 {
554 crm_err("Connection to the CIB terminated... exiting");
555 g_main_quit(mainloop);
556 return;
557 }
558
559 void
560 cibadmin_op_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
561 {
562 exit_code = rc;
563
564 if (rc != 0) {
565 crm_warn("Call %s failed (%d): %s", cib_action, rc, pcmk_strerror(rc));
566 fprintf(stderr, "Call %s failed (%d): %s\n", cib_action, rc, pcmk_strerror(rc));
567 print_xml_output(output);
568
569 } else if (safe_str_eq(cib_action, CIB_OP_QUERY) && output == NULL) {
570 crm_err("Output expected in query response");
571 crm_log_xml_err(msg, "no output");
572
573 } else if (output == NULL) {
574 crm_info("Call passed");
575
576 } else {
577 crm_info("Call passed");
578 print_xml_output(output);
579 }
580
581 if (call_id == request_id) {
582 g_main_quit(mainloop);
583
584 } else {
585 crm_info("Message was not the response we were looking for (%d vs. %d", call_id,
586 request_id);
587 }
588 }