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 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)
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(out, NULL, &cib_object);
151
152 if (rc != pcmk_rc_ok) {
153
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
227
228
229
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
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 pcmk__unregister_formats();
284 crm_exit(exit_code);
285 }