This source file includes following definitions.
- build_arg_context
- main
1
2
3
4
5
6
7
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) {
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)
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
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
246
247
248
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
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 }