root/tools/crm_verify.c

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

DEFINITIONS

This source file includes following definitions.
  1. build_arg_context
  2. main

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/crm.h>
  12 #include <crm/common/cmdline_internal.h>
  13 #include <crm/common/output_internal.h>
  14 
  15 #include <stdio.h>
  16 #include <sys/types.h>
  17 #include <unistd.h>
  18 
  19 #include <stdlib.h>
  20 #include <errno.h>
  21 #include <fcntl.h>
  22 #include <libgen.h>
  23 #include <glib.h>
  24 
  25 #include <crm/common/xml.h>
  26 #include <crm/common/util.h>
  27 #include <crm/msg_xml.h>
  28 #include <crm/cib.h>
  29 #include <crm/cib/internal.h>
  30 #include <crm/pengine/status.h>
  31 #include <pacemaker-internal.h>
  32 
  33 const char *SUMMARY = "Check a Pacemaker configuration for errors\n\n"
  34                       "Check the well-formedness of a complete Pacemaker XML configuration,\n"
  35                       "its conformance to the configured schema, and the presence of common\n"
  36                       "misconfigurations. Problems reported as errors must be fixed before the\n"
  37                       "cluster will work properly. It is left to the administrator to decide\n"
  38                       "whether to fix problems reported as warnings.";
  39 
  40 struct {
  41     char *cib_save;
  42     gboolean use_live_cib;
  43     char *xml_file;
  44     gboolean xml_stdin;
  45     char *xml_string;
  46 } options;
  47 
  48 static GOptionEntry data_entries[] = {
  49     { "live-check", 'L', 0, G_OPTION_ARG_NONE,
  50       &options.use_live_cib, "Check the configuration used by the running cluster",
  51       NULL },
  52     { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME,
  53       &options.xml_file, "Check the configuration in the named file",
  54       "FILE" },
  55     { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE,
  56       &options.xml_stdin, "Check the configuration piped in via stdin",
  57       NULL },
  58     { "xml-text", 'X', 0, G_OPTION_ARG_STRING,
  59       &options.xml_string, "Check the configuration in the supplied string",
  60       "XML" },
  61 
  62     { NULL }
  63 };
  64 
  65 static GOptionEntry addl_entries[] = {
  66     { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
  67       &options.cib_save, "Save verified XML to named file (most useful with -L)",
  68       "FILE" },
  69 
  70     { NULL }
  71 };
  72 
  73 static pcmk__supported_format_t formats[] = {
  74     PCMK__SUPPORTED_FORMAT_NONE,
  75     PCMK__SUPPORTED_FORMAT_TEXT,
  76     PCMK__SUPPORTED_FORMAT_XML,
  77     { NULL, NULL, NULL }
  78 };
  79 
  80 static GOptionContext *
  81 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
  82     GOptionContext *context = NULL;
  83 
  84     const char *description = "Examples:\n\n"
  85                               "Check the consistency of the configuration in the running cluster:\n\n"
  86                               "\tcrm_verify --live-check\n\n"
  87                               "Check the consistency of the configuration in a given file and "
  88                               "produce quiet output:\n\n"
  89                               "\tcrm_verify --xml-file file.xml --quiet\n\n"
  90                               "Check the consistency of the configuration in a given file and "
  91                               "produce verbose output:\n\n"
  92                               "\tcrm_verify --xml-file file.xml --verbose\n\n";
  93 
  94     GOptionEntry extra_prog_entries[] = {
  95         { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
  96           "Don't print verify information",
  97           NULL },
  98         { NULL }
  99     };
 100 
 101     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 102 
 103     pcmk__add_main_args(context, extra_prog_entries);
 104 
 105     g_option_context_set_description(context, description);
 106 
 107     pcmk__add_arg_group(context, "data", "Data sources:",
 108                         "Show data options", data_entries);
 109     pcmk__add_arg_group(context, "additional", "Additional options:",
 110                         "Show additional options", addl_entries);
 111 
 112     return context;
 113 }
 114 
 115 int
 116 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     xmlNode *cib_object = NULL;
 119     xmlNode *status = NULL;
 120 
 121     pcmk_scheduler_t *scheduler = NULL;
 122 
 123     int rc = pcmk_rc_ok;
 124     crm_exit_t exit_code = CRM_EX_OK;
 125 
 126     GError *error = NULL;
 127 
 128     pcmk__output_t *out = NULL;
 129 
 130     GOptionGroup *output_group = NULL;
 131     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 132     gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
 133     GOptionContext *context = build_arg_context(args, &output_group);
 134 
 135     pcmk__register_formats(output_group, formats);
 136     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 137         exit_code = CRM_EX_USAGE;
 138         goto done;
 139     }
 140 
 141     if (args->verbosity > 0) {
 142         args->verbosity -= args->quiet;
 143     }
 144 
 145     pcmk__cli_init_logging("crm_verify", args->verbosity);
 146 
 147     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 148     if (rc != pcmk_rc_ok) {
 149         exit_code = CRM_EX_ERROR;
 150         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 151                     args->output_ty, pcmk_rc_str(rc));
 152         goto done;
 153     }
 154 
 155     if (args->version) {
 156         out->version(out, false);
 157         goto done;
 158     }
 159 
 160     pcmk__register_lib_messages(out);
 161 
 162     pcmk__set_config_error_handler((pcmk__config_error_func) out->err, out);
 163     pcmk__set_config_warning_handler((pcmk__config_warning_func) out->err, out);
 164 
 165     crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
 166 
 167     if (options.use_live_cib) {
 168         crm_info("Reading XML from: live cluster");
 169         rc = cib__signon_query(out, NULL, &cib_object);
 170 
 171         if (rc != pcmk_rc_ok) {
 172             // cib__signon_query() outputs any relevant error
 173             goto done;
 174         }
 175 
 176     } else if (options.xml_file != NULL) {
 177         cib_object = filename2xml(options.xml_file);
 178         if (cib_object == NULL) {
 179             rc = ENODATA;
 180             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file);
 181             goto done;
 182         }
 183 
 184     } else if (options.xml_string != NULL) {
 185         cib_object = string2xml(options.xml_string);
 186         if (cib_object == NULL) {
 187             rc = ENODATA;
 188             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string);
 189             goto done;
 190         }
 191     } else if (options.xml_stdin) {
 192         cib_object = stdin2xml();
 193         if (cib_object == NULL) {
 194             rc = ENODATA;
 195             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN.");
 196             goto done;
 197         }
 198 
 199     } else {
 200         rc = ENODATA;
 201         g_set_error(&error, PCMK__RC_ERROR, rc,
 202                     "No configuration source specified.  Use --help for usage information.");
 203         goto done;
 204     }
 205 
 206     if (!pcmk__xe_is(cib_object, XML_TAG_CIB)) {
 207         rc = EBADMSG;
 208         g_set_error(&error, PCMK__RC_ERROR, rc,
 209                     "This tool can only check complete configurations (i.e. those starting with <cib>).");
 210         goto done;
 211     }
 212 
 213     if (options.cib_save != NULL) {
 214         write_xml_file(cib_object, options.cib_save, FALSE);
 215     }
 216 
 217     status = pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS);
 218     if (status == NULL) {
 219         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 220     }
 221 
 222     if (pcmk__validate_xml(cib_object, NULL, (xmlRelaxNGValidityErrorFunc) out->err, out) == FALSE) {
 223         pcmk__config_err("CIB did not pass schema validation");
 224         free_xml(cib_object);
 225         cib_object = NULL;
 226 
 227     } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 228         crm_config_error = TRUE;
 229         free_xml(cib_object);
 230         cib_object = NULL;
 231         out->err(out, "The cluster will NOT be able to use this configuration.\n"
 232                  "Please manually update the configuration to conform to the %s syntax.",
 233                  xml_latest_schema());
 234     }
 235 
 236     scheduler = pe_new_working_set();
 237     if (scheduler == NULL) {
 238         rc = errno;
 239         g_set_error(&error, PCMK__RC_ERROR, rc,
 240                     "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
 241         goto done;
 242     }
 243     scheduler->priv = out;
 244 
 245     /* Process the configuration to set crm_config_error/crm_config_warning.
 246      *
 247      * @TODO Some parts of the configuration are unpacked only when needed (for
 248      * example, action configuration), so we aren't necessarily checking those.
 249      */
 250     if (cib_object != NULL) {
 251         unsigned long long flags = pcmk_sched_no_counts|pcmk_sched_no_compat;
 252 
 253         if ((status == NULL) && !options.use_live_cib) {
 254             // No status available, so do minimal checks
 255             flags |= pcmk_sched_validate_only;
 256         }
 257         pcmk__schedule_actions(cib_object, flags, scheduler);
 258     }
 259     pe_free_working_set(scheduler);
 260 
 261     if (crm_config_error) {
 262         rc = pcmk_rc_schema_validation;
 263 
 264         if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
 265             g_set_error(&error, PCMK__RC_ERROR, rc,
 266                         "Errors found during check: config not valid");
 267         } else {
 268             g_set_error(&error, PCMK__RC_ERROR, rc,
 269                         "Errors found during check: config not valid\n-V may provide more details");
 270         } 
 271 
 272     } else if (crm_config_warning) {
 273         rc = pcmk_rc_schema_validation;
 274 
 275         if (args->verbosity > 0 || pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
 276             g_set_error(&error, PCMK__RC_ERROR, rc,
 277                         "Warnings found during check: config may not be valid");
 278         } else {
 279             g_set_error(&error, PCMK__RC_ERROR, rc,
 280                         "Warnings found during check: config may not be valid\n-V may provide more details");
 281         }
 282     }
 283 
 284   done:
 285     g_strfreev(processed_args);
 286     pcmk__free_arg_context(context);
 287     free(options.cib_save);
 288     free(options.xml_file);
 289     free(options.xml_string);
 290 
 291     if (exit_code == CRM_EX_OK) {
 292         exit_code = pcmk_rc2exitc(rc);
 293     }
 294 
 295     pcmk__output_and_clear_error(&error, out);
 296 
 297     if (out != NULL) {
 298         out->finish(out, exit_code, true, NULL);
 299         pcmk__output_free(out);
 300     }
 301 
 302     pcmk__unregister_formats();
 303     crm_exit(exit_code);
 304 }

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