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-2022 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 verbose output:\n\n"
  89                               "\tcrm_verify --xml-file file.xml --verbose\n\n";
  90 
  91     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
  92     g_option_context_set_description(context, description);
  93 
  94     pcmk__add_arg_group(context, "data", "Data sources:",
  95                         "Show data options", data_entries);
  96     pcmk__add_arg_group(context, "additional", "Additional options:",
  97                         "Show additional options", addl_entries);
  98 
  99     return context;
 100 }
 101 
 102 int
 103 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     xmlNode *cib_object = NULL;
 106     xmlNode *status = NULL;
 107 
 108     pe_working_set_t *data_set = NULL;
 109     const char *xml_tag = NULL;
 110 
 111     int rc = pcmk_rc_ok;
 112     crm_exit_t exit_code = CRM_EX_OK;
 113 
 114     GError *error = NULL;
 115 
 116     pcmk__output_t *out = NULL;
 117 
 118     GOptionGroup *output_group = NULL;
 119     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 120     gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
 121     GOptionContext *context = build_arg_context(args, &output_group);
 122 
 123     pcmk__register_formats(output_group, formats);
 124     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 125         exit_code = CRM_EX_USAGE;
 126         goto done;
 127     }
 128 
 129     pcmk__cli_init_logging("crm_verify", args->verbosity);
 130 
 131     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 132     if (rc != pcmk_rc_ok) {
 133         exit_code = CRM_EX_ERROR;
 134         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 135                     args->output_ty, pcmk_rc_str(rc));
 136         goto done;
 137     }
 138 
 139     if (args->version) {
 140         out->version(out, false);
 141         goto done;
 142     }
 143 
 144     pcmk__register_lib_messages(out);
 145 
 146     crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
 147 
 148     if (options.use_live_cib) {
 149         crm_info("Reading XML from: live cluster");
 150         rc = cib__signon_query(NULL, &cib_object);
 151 
 152         if (rc != pcmk_rc_ok) {
 153             g_set_error(&error, PCMK__RC_ERROR, rc, "CIB query failed: %s", pcmk_rc_str(rc));
 154             goto done;
 155         }
 156 
 157     } else if (options.xml_file != NULL) {
 158         cib_object = filename2xml(options.xml_file);
 159         if (cib_object == NULL) {
 160             rc = ENODATA;
 161             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file);
 162             goto done;
 163         }
 164 
 165     } else if (options.xml_string != NULL) {
 166         cib_object = string2xml(options.xml_string);
 167         if (cib_object == NULL) {
 168             rc = ENODATA;
 169             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string);
 170             goto done;
 171         }
 172     } else if (options.xml_stdin) {
 173         cib_object = stdin2xml();
 174         if (cib_object == NULL) {
 175             rc = ENODATA;
 176             g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN.");
 177             goto done;
 178         }
 179 
 180     } else {
 181         rc = ENODATA;
 182         g_set_error(&error, PCMK__RC_ERROR, rc,
 183                     "No configuration source specified.  Use --help for usage information.");
 184         goto done;
 185     }
 186 
 187     xml_tag = crm_element_name(cib_object);
 188     if (!pcmk__str_eq(xml_tag, XML_TAG_CIB, pcmk__str_casei)) {
 189         rc = EBADMSG;
 190         g_set_error(&error, PCMK__RC_ERROR, rc,
 191                     "This tool can only check complete configurations (i.e. those starting with <cib>).");
 192         goto done;
 193     }
 194 
 195     if (options.cib_save != NULL) {
 196         write_xml_file(cib_object, options.cib_save, FALSE);
 197     }
 198 
 199     status = pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS);
 200     if (status == NULL) {
 201         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 202     }
 203 
 204     if (validate_xml(cib_object, NULL, FALSE) == FALSE) {
 205         pcmk__config_err("CIB did not pass schema validation");
 206         free_xml(cib_object);
 207         cib_object = NULL;
 208 
 209     } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 210         crm_config_error = TRUE;
 211         free_xml(cib_object);
 212         cib_object = NULL;
 213         out->err(out, "The cluster will NOT be able to use this configuration.\n"
 214                  "Please manually update the configuration to conform to the %s syntax.",
 215                  xml_latest_schema());
 216     }
 217 
 218     data_set = pe_new_working_set();
 219     if (data_set == NULL) {
 220         rc = errno;
 221         crm_perror(LOG_CRIT, "Unable to allocate working set");
 222         goto done;
 223     }
 224     data_set->priv = out;
 225 
 226     /* Process the configuration to set crm_config_error/crm_config_warning.
 227      *
 228      * @TODO Some parts of the configuration are unpacked only when needed (for
 229      * example, action configuration), so we aren't necessarily checking those.
 230      */
 231     if (cib_object != NULL) {
 232         unsigned long long flags = pe_flag_no_counts|pe_flag_no_compat;
 233 
 234         if ((status == NULL) && !options.use_live_cib) {
 235             // No status available, so do minimal checks
 236             flags |= pe_flag_check_config;
 237         }
 238         pcmk__schedule_actions(cib_object, flags, data_set);
 239     }
 240     pe_free_working_set(data_set);
 241 
 242     if (crm_config_error) {
 243         rc = pcmk_rc_schema_validation;
 244 
 245         if (args->verbosity > 0) {
 246             g_set_error(&error, PCMK__RC_ERROR, rc,
 247                         "Errors found during check: config not valid");
 248         } else {
 249             g_set_error(&error, PCMK__RC_ERROR, rc,
 250                         "Errors found during check: config not valid\n-V may provide more details");
 251         }
 252 
 253     } else if (crm_config_warning) {
 254         rc = pcmk_rc_schema_validation;
 255 
 256         if (args->verbosity > 0) {
 257             g_set_error(&error, PCMK__RC_ERROR, rc,
 258                         "Warnings found during check: config may not be valid");
 259         } else {
 260             g_set_error(&error, PCMK__RC_ERROR, rc,
 261                         "Warnings found during check: config may not be valid\n-V may provide more details");
 262         }
 263     }
 264 
 265   done:
 266     g_strfreev(processed_args);
 267     pcmk__free_arg_context(context);
 268     free(options.cib_save);
 269     free(options.xml_file);
 270     free(options.xml_string);
 271 
 272     if (exit_code == CRM_EX_OK) {
 273         exit_code = pcmk_rc2exitc(rc);
 274     }
 275 
 276     pcmk__output_and_clear_error(error, NULL);
 277 
 278     if (out != NULL) {
 279         out->finish(out, exit_code, true, NULL);
 280         pcmk__output_free(out);
 281     }
 282 
 283     crm_exit(exit_code);
 284 }

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