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/pengine/status.h>
30 #include <pacemaker-internal.h>
31
32 const char *SUMMARY = "Check a Pacemaker configuration for errors\n\n"
33 "Check the well-formedness of a complete Pacemaker XML configuration,\n"
34 "its conformance to the configured schema, and the presence of common\n"
35 "misconfigurations. Problems reported as errors must be fixed before the\n"
36 "cluster will work properly. It is left to the administrator to decide\n"
37 "whether to fix problems reported as warnings.";
38
39 struct {
40 char *cib_save;
41 gboolean use_live_cib;
42 char *xml_file;
43 gboolean xml_stdin;
44 char *xml_string;
45 } options;
46
47 extern gboolean stage0(pe_working_set_t * data_set);
48
49 static GOptionEntry data_entries[] = {
50 { "live-check", 'L', 0, G_OPTION_ARG_NONE,
51 &options.use_live_cib, "Check the configuration used by the running cluster",
52 NULL },
53 { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME,
54 &options.xml_file, "Check the configuration in the named file",
55 "FILE" },
56 { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE,
57 &options.xml_stdin, "Check the configuration piped in via stdin",
58 NULL },
59 { "xml-text", 'X', 0, G_OPTION_ARG_STRING,
60 &options.xml_string, "Check the configuration in the supplied string",
61 "XML" },
62
63 { NULL }
64 };
65
66 static GOptionEntry addl_entries[] = {
67 { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
68 &options.cib_save, "Save verified XML to named file (most useful with -L)",
69 "FILE" },
70
71 { NULL }
72 };
73
74 static pcmk__supported_format_t formats[] = {
75 PCMK__SUPPORTED_FORMAT_NONE,
76 PCMK__SUPPORTED_FORMAT_TEXT,
77 PCMK__SUPPORTED_FORMAT_XML,
78 { NULL, NULL, NULL }
79 };
80
81 static GOptionContext *
82 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
83 GOptionContext *context = NULL;
84
85 const char *description = "Examples:\n\n"
86 "Check the consistency of the configuration in the running cluster:\n\n"
87 "\tcrm_verify --live-check\n\n"
88 "Check the consistency of the configuration in a given file and "
89 "produce verbose output:\n\n"
90 "\tcrm_verify --xml-file file.xml --verbose\n\n";
91
92 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
93 g_option_context_set_description(context, description);
94
95 pcmk__add_arg_group(context, "data", "Data sources:",
96 "Show data options", data_entries);
97 pcmk__add_arg_group(context, "additional", "Additional options:",
98 "Show additional options", addl_entries);
99
100 return context;
101 }
102
103 int
104 main(int argc, char **argv)
105 {
106 xmlNode *cib_object = NULL;
107 xmlNode *status = NULL;
108
109 pe_working_set_t *data_set = NULL;
110 cib_t *cib_conn = NULL;
111 const char *xml_tag = NULL;
112
113 int rc = pcmk_rc_ok;
114 crm_exit_t exit_code = CRM_EX_OK;
115
116 GError *error = NULL;
117
118 pcmk__output_t *out = NULL;
119
120 GOptionGroup *output_group = NULL;
121 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
122 gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
123 GOptionContext *context = build_arg_context(args, &output_group);
124
125 pcmk__register_formats(output_group, formats);
126 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
127 exit_code = CRM_EX_USAGE;
128 goto done;
129 }
130
131 pcmk__cli_init_logging("crm_verify", args->verbosity);
132
133 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
134 if (rc != pcmk_rc_ok) {
135 exit_code = CRM_EX_ERROR;
136 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
137 args->output_ty, pcmk_rc_str(rc));
138 goto done;
139 }
140
141 if (args->version) {
142 out->version(out, false);
143 goto done;
144 }
145
146 pcmk__register_lib_messages(out);
147
148 crm_info("=#=#=#=#= Getting XML =#=#=#=#=");
149
150 if (options.use_live_cib) {
151 cib_conn = cib_new();
152 rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
153 rc = pcmk_legacy2rc(rc);
154 }
155
156 if (options.use_live_cib) {
157 if (rc == pcmk_rc_ok) {
158 int options = cib_scope_local | cib_sync_call;
159
160 crm_info("Reading XML from: live cluster");
161 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, options);
162 rc = pcmk_legacy2rc(rc);
163 }
164
165 if (rc != pcmk_rc_ok) {
166 g_set_error(&error, PCMK__RC_ERROR, rc, "Live CIB query failed: %s", pcmk_rc_str(rc));
167 goto done;
168 }
169 if (cib_object == NULL) {
170 rc = ENOMSG;
171 g_set_error(&error, PCMK__RC_ERROR, rc, "Live CIB query failed: empty result");
172 goto done;
173 }
174
175 } else if (options.xml_file != NULL) {
176 cib_object = filename2xml(options.xml_file);
177 if (cib_object == NULL) {
178 rc = ENODATA;
179 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file);
180 goto done;
181 }
182
183 } else if (options.xml_string != NULL) {
184 cib_object = string2xml(options.xml_string);
185 if (cib_object == NULL) {
186 rc = ENODATA;
187 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string);
188 goto done;
189 }
190 } else if (options.xml_stdin) {
191 cib_object = stdin2xml();
192 if (cib_object == NULL) {
193 rc = ENODATA;
194 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN.");
195 goto done;
196 }
197
198 } else {
199 rc = ENODATA;
200 g_set_error(&error, PCMK__RC_ERROR, rc,
201 "No configuration source specified. Use --help for usage information.");
202 goto done;
203 }
204
205 xml_tag = crm_element_name(cib_object);
206 if (!pcmk__str_eq(xml_tag, XML_TAG_CIB, pcmk__str_casei)) {
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 = get_object_root(XML_CIB_TAG_STATUS, cib_object);
218 if (status == NULL) {
219 create_xml_node(cib_object, XML_CIB_TAG_STATUS);
220 }
221
222 if (validate_xml(cib_object, NULL, FALSE) == 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 data_set = pe_new_working_set();
237 if (data_set == NULL) {
238 rc = errno;
239 crm_perror(LOG_CRIT, "Unable to allocate working set");
240 goto done;
241 }
242 pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
243 data_set->priv = out;
244
245 if (cib_object == NULL) {
246 } else if (status != NULL || options.use_live_cib) {
247
248 pcmk__schedule_actions(data_set, cib_object, NULL);
249
250 } else {
251 data_set->now = crm_time_new(NULL);
252 data_set->input = cib_object;
253 stage0(data_set);
254 }
255 pe_free_working_set(data_set);
256
257 if (crm_config_error) {
258 rc = pcmk_rc_schema_validation;
259
260 if (args->verbosity > 0) {
261 g_set_error(&error, PCMK__RC_ERROR, rc,
262 "Errors found during check: config not valid");
263 } else {
264 g_set_error(&error, PCMK__RC_ERROR, rc,
265 "Errors found during check: config not valid\n-V may provide more details");
266 }
267
268 } else if (crm_config_warning) {
269 rc = pcmk_rc_schema_validation;
270
271 if (args->verbosity > 0) {
272 g_set_error(&error, PCMK__RC_ERROR, rc,
273 "Warnings found during check: config may not be valid");
274 } else {
275 g_set_error(&error, PCMK__RC_ERROR, rc,
276 "Warnings found during check: config may not be valid\n-V may provide more details");
277 }
278 }
279
280 if (options.use_live_cib && cib_conn) {
281 cib_conn->cmds->signoff(cib_conn);
282 cib_delete(cib_conn);
283 }
284
285 done:
286 g_strfreev(processed_args);
287 pcmk__free_arg_context(context);
288 free(options.cib_save);
289 free(options.xml_file);
290 free(options.xml_string);
291
292 if (exit_code == CRM_EX_OK) {
293 exit_code = pcmk_rc2exitc(rc);
294 }
295
296 pcmk__output_and_clear_error(error, NULL);
297
298 if (out != NULL) {
299 out->finish(out, exit_code, true, NULL);
300 pcmk__output_free(out);
301 }
302
303 crm_exit(exit_code);
304 }