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 extern gboolean stage0(pe_working_set_t * data_set);
49
50 static GOptionEntry data_entries[] = {
51 { "live-check", 'L', 0, G_OPTION_ARG_NONE,
52 &options.use_live_cib, "Check the configuration used by the running cluster",
53 NULL },
54 { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME,
55 &options.xml_file, "Check the configuration in the named file",
56 "FILE" },
57 { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE,
58 &options.xml_stdin, "Check the configuration piped in via stdin",
59 NULL },
60 { "xml-text", 'X', 0, G_OPTION_ARG_STRING,
61 &options.xml_string, "Check the configuration in the supplied string",
62 "XML" },
63
64 { NULL }
65 };
66
67 static GOptionEntry addl_entries[] = {
68 { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
69 &options.cib_save, "Save verified XML to named file (most useful with -L)",
70 "FILE" },
71
72 { NULL }
73 };
74
75 static pcmk__supported_format_t formats[] = {
76 PCMK__SUPPORTED_FORMAT_NONE,
77 PCMK__SUPPORTED_FORMAT_TEXT,
78 PCMK__SUPPORTED_FORMAT_XML,
79 { NULL, NULL, NULL }
80 };
81
82 static GOptionContext *
83 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
84 GOptionContext *context = NULL;
85
86 const char *description = "Examples:\n\n"
87 "Check the consistency of the configuration in the running cluster:\n\n"
88 "\tcrm_verify --live-check\n\n"
89 "Check the consistency of the configuration in a given file and "
90 "produce verbose output:\n\n"
91 "\tcrm_verify --xml-file file.xml --verbose\n\n";
92
93 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
94 g_option_context_set_description(context, description);
95
96 pcmk__add_arg_group(context, "data", "Data sources:",
97 "Show data options", data_entries);
98 pcmk__add_arg_group(context, "additional", "Additional options:",
99 "Show additional options", addl_entries);
100
101 return context;
102 }
103
104 int
105 main(int argc, char **argv)
106 {
107 xmlNode *cib_object = NULL;
108 xmlNode *status = NULL;
109
110 pe_working_set_t *data_set = 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 crm_info("Reading XML from: live cluster");
152 rc = cib__signon_query(NULL, &cib_object);
153
154 if (rc != pcmk_rc_ok) {
155 g_set_error(&error, PCMK__RC_ERROR, rc, "CIB query failed: %s", pcmk_rc_str(rc));
156 goto done;
157 }
158
159 } else if (options.xml_file != NULL) {
160 cib_object = filename2xml(options.xml_file);
161 if (cib_object == NULL) {
162 rc = ENODATA;
163 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input file: %s", options.xml_file);
164 goto done;
165 }
166
167 } else if (options.xml_string != NULL) {
168 cib_object = string2xml(options.xml_string);
169 if (cib_object == NULL) {
170 rc = ENODATA;
171 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input string: %s", options.xml_string);
172 goto done;
173 }
174 } else if (options.xml_stdin) {
175 cib_object = stdin2xml();
176 if (cib_object == NULL) {
177 rc = ENODATA;
178 g_set_error(&error, PCMK__RC_ERROR, rc, "Couldn't parse input from STDIN.");
179 goto done;
180 }
181
182 } else {
183 rc = ENODATA;
184 g_set_error(&error, PCMK__RC_ERROR, rc,
185 "No configuration source specified. Use --help for usage information.");
186 goto done;
187 }
188
189 xml_tag = crm_element_name(cib_object);
190 if (!pcmk__str_eq(xml_tag, XML_TAG_CIB, pcmk__str_casei)) {
191 rc = EBADMSG;
192 g_set_error(&error, PCMK__RC_ERROR, rc,
193 "This tool can only check complete configurations (i.e. those starting with <cib>).");
194 goto done;
195 }
196
197 if (options.cib_save != NULL) {
198 write_xml_file(cib_object, options.cib_save, FALSE);
199 }
200
201 status = get_object_root(XML_CIB_TAG_STATUS, cib_object);
202 if (status == NULL) {
203 create_xml_node(cib_object, XML_CIB_TAG_STATUS);
204 }
205
206 if (validate_xml(cib_object, NULL, FALSE) == FALSE) {
207 pcmk__config_err("CIB did not pass schema validation");
208 free_xml(cib_object);
209 cib_object = NULL;
210
211 } else if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
212 crm_config_error = TRUE;
213 free_xml(cib_object);
214 cib_object = NULL;
215 out->err(out, "The cluster will NOT be able to use this configuration.\n"
216 "Please manually update the configuration to conform to the %s syntax.",
217 xml_latest_schema());
218 }
219
220 data_set = pe_new_working_set();
221 if (data_set == NULL) {
222 rc = errno;
223 crm_perror(LOG_CRIT, "Unable to allocate working set");
224 goto done;
225 }
226 pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
227 data_set->priv = out;
228
229 if (cib_object == NULL) {
230 } else if (status != NULL || options.use_live_cib) {
231
232 pcmk__schedule_actions(data_set, cib_object, NULL);
233
234 } else {
235 data_set->now = crm_time_new(NULL);
236 data_set->input = cib_object;
237 stage0(data_set);
238 }
239 pe_free_working_set(data_set);
240
241 if (crm_config_error) {
242 rc = pcmk_rc_schema_validation;
243
244 if (args->verbosity > 0) {
245 g_set_error(&error, PCMK__RC_ERROR, rc,
246 "Errors found during check: config not valid");
247 } else {
248 g_set_error(&error, PCMK__RC_ERROR, rc,
249 "Errors found during check: config not valid\n-V may provide more details");
250 }
251
252 } else if (crm_config_warning) {
253 rc = pcmk_rc_schema_validation;
254
255 if (args->verbosity > 0) {
256 g_set_error(&error, PCMK__RC_ERROR, rc,
257 "Warnings found during check: config may not be valid");
258 } else {
259 g_set_error(&error, PCMK__RC_ERROR, rc,
260 "Warnings found during check: config may not be valid\n-V may provide more details");
261 }
262 }
263
264 done:
265 g_strfreev(processed_args);
266 pcmk__free_arg_context(context);
267 free(options.cib_save);
268 free(options.xml_file);
269 free(options.xml_string);
270
271 if (exit_code == CRM_EX_OK) {
272 exit_code = pcmk_rc2exitc(rc);
273 }
274
275 pcmk__output_and_clear_error(error, NULL);
276
277 if (out != NULL) {
278 out->finish(out, exit_code, true, NULL);
279 pcmk__output_free(out);
280 }
281
282 crm_exit(exit_code);
283 }