diff --git a/tools/Makefile.am b/tools/Makefile.am index de64c93c2f..a7f35cf387 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,170 +1,171 @@ # # Copyright 2004-2019 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(top_srcdir)/mk/common.mk if BUILD_SYSTEMD systemdsystemunit_DATA = crm_mon.service endif noinst_HEADERS = crm_mon.h crm_resource.h pcmkdir = $(datadir)/$(PACKAGE) pcmk_DATA = report.common report.collector sbin_SCRIPTS = crm_report crm_standby crm_master crm_failcount if BUILD_CIBSECRETS sbin_SCRIPTS += cibsecret endif noinst_SCRIPTS = pcmk_simtimes EXTRA_DIST = crm_diff.8.inc \ crm_error.8.inc \ crm_mon.sysconfig \ crm_mon.8.inc \ crm_node.8.inc \ crm_resource.8.inc \ crm_rule.8.inc \ crm_simulate.8.inc \ + crm_verify.8.inc \ crmadmin.8.inc \ fix-manpages \ stonith_admin.8.inc sbin_PROGRAMS = attrd_updater \ cibadmin \ crmadmin \ crm_simulate \ crm_attribute \ crm_diff \ crm_error \ crm_mon \ crm_node \ crm_resource \ crm_rule \ crm_shadow \ crm_verify \ crm_ticket \ iso8601 \ stonith_admin if BUILD_SERVICELOG sbin_PROGRAMS += notifyServicelogEvent endif if BUILD_OPENIPMI_SERVICELOG sbin_PROGRAMS += ipmiservicelogd endif ## SOURCES # A few tools are just thin wrappers around crm_attribute. # This makes their help get updated when crm_attribute changes # (see mk/common.mk). MAN8DEPS = crm_attribute crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la crm_error_SOURCES = crm_error.c crm_error_LDADD = $(top_builddir)/lib/common/libcrmcommon.la cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_shadow_SOURCES = crm_shadow.c crm_shadow_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_node_SOURCES = crm_node.c crm_node_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_simulate_SOURCES = crm_simulate.c crm_simulate_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_diff_SOURCES = crm_diff.c crm_diff_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_mon_SOURCES = crm_mon.c crm_mon_curses.c crm_mon_print.c crm_mon_runtime.c crm_mon_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la \ $(CURSESLIBS) crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_resource_SOURCES = crm_resource.c \ crm_resource_ban.c \ crm_resource_print.c \ crm_resource_runtime.c crm_resource_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/lrmd/liblrmd.la \ $(top_builddir)/lib/services/libcrmservice.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la crm_rule_SOURCES = crm_rule.c crm_rule_LDADD = $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/common/libcrmcommon.la iso8601_SOURCES = iso8601.c iso8601_LDADD = $(top_builddir)/lib/common/libcrmcommon.la attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(top_builddir)/lib/common/libcrmcommon.la crm_ticket_SOURCES = crm_ticket.c crm_ticket_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/common/libcrmcommon.la stonith_admin_SOURCES = stonith_admin.c stonith_admin_LDADD = $(top_builddir)/lib/pacemaker/libpacemaker.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/lib/common/libcrmcommon.la if BUILD_SERVICELOG notifyServicelogEvent_SOURCES = notifyServicelogEvent.c notifyServicelogEvent_CFLAGS = $(SERVICELOG_CFLAGS) notifyServicelogEvent_LDADD = $(top_builddir)/lib/common/libcrmcommon.la $(SERVICELOG_LIBS) endif if BUILD_OPENIPMI_SERVICELOG ipmiservicelogd_SOURCES = ipmiservicelogd.c ipmiservicelogd_CFLAGS = $(OPENIPMI_SERVICELOG_CFLAGS) $(SERVICELOG_CFLAGS) ipmiservicelogd_LDFLAGS = $(top_builddir)/lib/common/libcrmcommon.la $(OPENIPMI_SERVICELOG_LIBS) $(SERVICELOG_LIBS) endif CLEANFILES = $(man8_MANS) diff --git a/tools/crm_verify.8.inc b/tools/crm_verify.8.inc new file mode 100644 index 0000000000..27b0a55fb8 --- /dev/null +++ b/tools/crm_verify.8.inc @@ -0,0 +1,5 @@ +[=synopsis] +crm_verify [] + +/reported as warnings./ +.SH OPTIONS diff --git a/tools/crm_verify.c b/tools/crm_verify.c index bb6932d3ba..1b0247c781 100644 --- a/tools/crm_verify.c +++ b/tools/crm_verify.c @@ -1,342 +1,278 @@ /* * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +const char *SUMMARY = "Check a Pacemaker configuration for errors\n\n" + "Check the well-formedness of a complete Pacemaker XML configuration,\n" + "its conformance to the configured schema, and the presence of common\n" + "misconfigurations. Problems reported as errors must be fixed before the\n" + "cluster will work properly. It is left to the administrator to decide\n" + "whether to fix problems reported as warnings."; + struct { char *cib_save; gboolean use_live_cib; char *xml_file; gboolean xml_stdin; char *xml_string; } options; extern gboolean stage0(pe_working_set_t * data_set); -static pcmk__cli_option_t long_options[] = { - // long option, argument type, storage, short option, description, flags - { - "help", no_argument, NULL, '?', - "\tThis text", pcmk__option_default - }, - { - "version", no_argument, NULL, '$', - "\tVersion information", pcmk__option_default - }, - { - "verbose", no_argument, NULL, 'V', - "\tSpecify multiple times to increase debug output\n", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nData sources:", pcmk__option_default - }, - { - "live-check", no_argument, NULL, 'L', - "Check the configuration used by the running cluster\n", - pcmk__option_default - }, - { - "xml-file", required_argument, NULL, 'x', - "Check the configuration in the named file", pcmk__option_default - }, - { - "xml-text", required_argument, NULL, 'X', - "Check the configuration in the supplied string", pcmk__option_default - }, - { - "xml-pipe", no_argument, NULL, 'p', - "Check the configuration piped in via stdin", pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nAdditional Options:", pcmk__option_default - }, - { - "save-xml", required_argument, 0, 'S', - "Save verified XML to named file (most useful with -L)", - pcmk__option_default - }, - { - "-spacer-", no_argument, NULL, '-', - "\nExamples:", pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - "Check the consistency of the configuration in the running cluster:", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_verify --live-check", pcmk__option_example - }, - { - "-spacer-", no_argument, NULL, '-', - "Check the consistency of the configuration in a given file and " - "produce verbose output:", - pcmk__option_paragraph - }, - { - "-spacer-", no_argument, NULL, '-', - " crm_verify --xml-file file.xml --verbose", pcmk__option_example - }, - {0, 0, 0, 0} +static GOptionEntry data_entries[] = { + { "live-check", 'L', 0, G_OPTION_ARG_NONE, + &options.use_live_cib, "Check the configuration used by the running cluster", + NULL }, + { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME, + &options.xml_file, "Check the configuration in the named file", + "FILE" }, + { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE, + &options.xml_string, "Check the configuration piped in via stdin", + NULL }, + { "xml-text", 'X', 0, G_OPTION_ARG_STRING, + &options.xml_string, "Check the configuration in the supplied string", + "XML" }, + + { NULL } +}; + +static GOptionEntry addl_entries[] = { + { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, + &options.cib_save, "Save verified XML to named file (most useful with -L)", + "FILE" }, + + { NULL } }; +static GOptionContext * +build_arg_context(pcmk__common_args_t *args) { + GOptionContext *context = NULL; + + const char *description = "Examples:\n\n" + "Check the consistency of the configuration in the running cluster:\n\n" + "\tcrm_verify --live-check\n\n" + "Check the consistency of the configuration in a given file and " + "produce verbose output:\n\n" + "\tcrm_verify --xml-file file.xml --verbose\n\n"; + + context = pcmk__build_arg_context(args, NULL, NULL, NULL); + g_option_context_set_description(context, description); + + pcmk__add_arg_group(context, "data", "Data sources:", + "Show data options", data_entries); + pcmk__add_arg_group(context, "additional", "Additional options:", + "Show additional options", addl_entries); + + return context; +} + int main(int argc, char **argv) { xmlNode *cib_object = NULL; xmlNode *status = NULL; - int argerr = 0; - int flag; - int option_index = 0; pe_working_set_t *data_set = NULL; cib_t *cib_conn = NULL; + const char *xml_tag = NULL; + int rc = pcmk_rc_ok; + crm_exit_t exit_code = CRM_EX_OK; GError *error = NULL; - bool verbose = FALSE; - const char *xml_tag = NULL; - - pcmk__cli_init_logging("crm_verify", 0); - pcmk__set_cli_options(NULL, "[options]", long_options, - "check a Pacemaker configuration for errors\n\n" - "Check the well-formedness of a complete Pacemaker " - "XML configuration,\nits conformance to the " - "configured schema, and the presence of common\n" - "misconfigurations. Problems reported as errors " - "must be fixed before the\ncluster will work " - "properly. It is left to the administrator to decide" - "\nwhether to fix problems reported as warnings."); - - while (1) { - flag = pcmk__next_cli_option(argc, argv, &option_index, NULL); - if (flag == -1) - break; - - switch (flag) { - case 'X': - crm_trace("Option %c => %s", flag, optarg); - options.xml_string = optarg; - break; - case 'x': - crm_trace("Option %c => %s", flag, optarg); - options.xml_file = optarg; - break; - case 'p': - options.xml_stdin = TRUE; - break; - case 'S': - options.cib_save = optarg; - break; - case 'V': - verbose = TRUE; - crm_bump_log_level(argc, argv); - break; - case 'L': - options.use_live_cib = TRUE; - break; - case '$': - case '?': - pcmk__cli_help(flag, CRM_EX_OK); - break; - default: - fprintf(stderr, "Option -%c is not yet supported\n", flag); - ++argerr; - break; - } - } + pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); + gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX"); + GOptionContext *context = build_arg_context(args); - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) { - printf("%s ", argv[optind++]); - } - printf("\n"); + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; } - if (optind > argc) { - ++argerr; - } + pcmk__cli_init_logging("crm_verify", args->verbosity); - if (argerr) { - crm_err("%d errors in option parsing", argerr); - pcmk__cli_help(flag, CRM_EX_USAGE); + if (args->version) { + g_strfreev(processed_args); + pcmk__free_arg_context(context); + /* FIXME: When crm_verify is converted to use formatted output, this can go. */ + pcmk__cli_help('v', CRM_EX_USAGE); } crm_info("=#=#=#=#= Getting XML =#=#=#=#="); if (options.use_live_cib) { cib_conn = cib_new(); rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); rc = pcmk_legacy2rc(rc); } if (options.use_live_cib) { if (rc == pcmk_rc_ok) { int options = cib_scope_local | cib_sync_call; crm_info("Reading XML from: live cluster"); rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, options); rc = pcmk_legacy2rc(rc); } if (rc != pcmk_rc_ok) { g_set_error(&error, PCMK__RC_ERROR, rc, "Live CIB query failed: %s", pcmk_rc_str(rc)); goto done; } if (cib_object == NULL) { rc = ENOMSG; g_set_error(&error, PCMK__RC_ERROR, rc, "Live CIB query failed: empty result"); goto done; } } else if (options.xml_file != NULL) { cib_object = filename2xml(options.xml_file); if (cib_object == NULL) { rc = ENODATA; g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file); goto done; } } else if (options.xml_string != NULL) { cib_object = string2xml(options.xml_string); if (cib_object == NULL) { rc = ENODATA; g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string); goto done; } } else if (options.xml_stdin) { cib_object = stdin2xml(); if (cib_object == NULL) { rc = ENODATA; g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN."); goto done; } } else { rc = ENODATA; g_set_error(&error, PCMK__RC_ERROR, rc, "No configuration source specified. Use --help for usage information."); goto done; } xml_tag = crm_element_name(cib_object); if (!pcmk__str_eq(xml_tag, XML_TAG_CIB, pcmk__str_casei)) { rc = EBADMSG; g_set_error(&error, PCMK__RC_ERROR, rc, "This tool can only check complete configurations (i.e. those starting with )."); goto done; } if (options.cib_save != NULL) { write_xml_file(cib_object, options.cib_save, FALSE); } status = get_object_root(XML_CIB_TAG_STATUS, cib_object); if (status == NULL) { create_xml_node(cib_object, XML_CIB_TAG_STATUS); } if (validate_xml(cib_object, NULL, FALSE) == FALSE) { pcmk__config_err("CIB did not pass schema validation"); free_xml(cib_object); cib_object = NULL; } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) { pcmk__config_error = true; free_xml(cib_object); cib_object = NULL; fprintf(stderr, "The cluster will NOT be able to use this configuration.\n"); fprintf(stderr, "Please manually update the configuration to conform to the %s syntax.\n", xml_latest_schema()); } data_set = pe_new_working_set(); if (data_set == NULL) { rc = errno; crm_perror(LOG_CRIT, "Unable to allocate working set"); goto done; } pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat); if (cib_object == NULL) { } else if (status != NULL || options.use_live_cib) { /* live queries will always have a status section and can do a full simulation */ pcmk__schedule_actions(data_set, cib_object, NULL); } else { data_set->now = crm_time_new(NULL); data_set->input = cib_object; stage0(data_set); } pe_free_working_set(data_set); if (pcmk__config_error) { rc = pcmk_rc_schema_validation; - if (verbose) { + if (args->verbosity > 0) { g_set_error(&error, PCMK__RC_ERROR, rc, "Errors found during check: config not valid"); } else { g_set_error(&error, PCMK__RC_ERROR, rc, "Errors found during check: config not valid\n-V may provide more details"); } } else if (pcmk__config_warning) { rc = pcmk_rc_schema_validation; - if (verbose) { + if (args->verbosity > 0) { g_set_error(&error, PCMK__RC_ERROR, rc, "Warnings found during check: config may not be valid"); } else { g_set_error(&error, PCMK__RC_ERROR, rc, "Warnings found during check: config may not be valid\n-V may provide more details"); } } if (options.use_live_cib && cib_conn) { cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); } done: - if (error != NULL) { - fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message); - g_clear_error(&error); - } - + g_strfreev(processed_args); + pcmk__free_arg_context(context); free(options.cib_save); free(options.xml_file); free(options.xml_string); - crm_exit(pcmk_rc2exitc(rc)); + pcmk__output_and_clear_error(error, NULL); + + if (exit_code != CRM_EX_OK) { + crm_exit(exit_code); + } else { + crm_exit(pcmk_rc2exitc(rc)); + } }