Line data Source code
1 : /*
2 : * Copyright 2004-2024 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 :
12 : #include <stdbool.h>
13 : #include <stddef.h>
14 : #include <stdint.h>
15 :
16 : #include <crm/cib/internal.h>
17 : #include <crm/common/output.h>
18 : #include <crm/common/results.h>
19 : #include <crm/fencing/internal.h>
20 : #include <crm/pengine/internal.h>
21 : #include <crm/stonith-ng.h> // stonith__register_messages()
22 : #include <pacemaker.h>
23 : #include <pacemaker-internal.h>
24 :
25 : static stonith_t *
26 0 : fencing_connect(void)
27 : {
28 0 : stonith_t *st = stonith_api_new();
29 0 : int rc = pcmk_rc_ok;
30 :
31 0 : if (st == NULL) {
32 0 : return NULL;
33 : }
34 :
35 0 : rc = st->cmds->connect(st, crm_system_name, NULL);
36 0 : if (rc == pcmk_rc_ok) {
37 0 : return st;
38 : } else {
39 0 : stonith_api_delete(st);
40 0 : return NULL;
41 : }
42 : }
43 :
44 : /*!
45 : * \internal
46 : * \brief Output the cluster status given a fencer and CIB connection
47 : *
48 : * \param[in,out] out Output object
49 : * \param[in,out] stonith Fencer connection
50 : * \param[in,out] cib CIB connection
51 : * \param[in] current_cib Current CIB XML
52 : * \param[in] pcmkd_state \p pacemakerd state
53 : * \param[in] fence_history How much of the fencing history to output
54 : * \param[in] show Group of \p pcmk_section_e flags
55 : * \param[in] show_opts Group of \p pcmk_show_opt_e flags
56 : * \param[in] only_node If a node name or tag, include only the
57 : * matching node(s) (if any) in the output.
58 : * If \p "*" or \p NULL, include all nodes
59 : * in the output.
60 : * \param[in] only_rsc If a resource ID or tag, include only the
61 : * matching resource(s) (if any) in the
62 : * output. If \p "*" or \p NULL, include all
63 : * resources in the output.
64 : * \param[in] neg_location_prefix Prefix denoting a ban in a constraint ID
65 : * \param[in] simple_output Whether to use a simple output format.
66 : * Note: This is for use by \p crm_mon only
67 : * and is planned to be deprecated.
68 : *
69 : * \return Standard Pacemaker return code
70 : */
71 : int
72 0 : pcmk__output_cluster_status(pcmk__output_t *out, stonith_t *stonith, cib_t *cib,
73 : xmlNode *current_cib,
74 : enum pcmk_pacemakerd_state pcmkd_state,
75 : enum pcmk__fence_history fence_history,
76 : uint32_t show, uint32_t show_opts,
77 : const char *only_node, const char *only_rsc,
78 : const char *neg_location_prefix, bool simple_output)
79 : {
80 0 : xmlNode *cib_copy = pcmk__xml_copy(NULL, current_cib);
81 0 : stonith_history_t *stonith_history = NULL;
82 0 : int history_rc = 0;
83 0 : pcmk_scheduler_t *scheduler = NULL;
84 0 : GList *unames = NULL;
85 0 : GList *resources = NULL;
86 :
87 0 : int rc = pcmk_rc_ok;
88 :
89 0 : if (!pcmk__update_configured_schema(&cib_copy, false)) {
90 0 : cib__clean_up_connection(&cib);
91 0 : free_xml(cib_copy);
92 0 : rc = pcmk_rc_schema_validation;
93 0 : out->err(out, "Upgrade failed: %s", pcmk_rc_str(rc));
94 0 : return rc;
95 : }
96 :
97 : /* get the stonith-history if there is evidence we need it */
98 0 : if (fence_history != pcmk__fence_history_none) {
99 0 : history_rc = pcmk__get_fencing_history(stonith, &stonith_history,
100 : fence_history);
101 : }
102 :
103 0 : scheduler = pe_new_working_set();
104 0 : pcmk__mem_assert(scheduler);
105 0 : pcmk__set_scheduler_flags(scheduler, pcmk_sched_no_compat);
106 :
107 0 : scheduler->input = cib_copy;
108 0 : scheduler->priv = out;
109 0 : cluster_status(scheduler);
110 :
111 0 : if ((cib->variant == cib_native) && pcmk_is_set(show, pcmk_section_times)) {
112 0 : if (pcmk__our_nodename == NULL) {
113 : // Currently used only in the times section
114 0 : pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
115 : }
116 0 : scheduler->localhost = pcmk__our_nodename;
117 : }
118 :
119 : /* Unpack constraints if any section will need them
120 : * (tickets may be referenced in constraints but not granted yet,
121 : * and bans need negative location constraints) */
122 0 : if (pcmk_is_set(show, pcmk_section_bans)
123 0 : || pcmk_is_set(show, pcmk_section_tickets)) {
124 0 : pcmk__unpack_constraints(scheduler);
125 : }
126 :
127 0 : unames = pe__build_node_name_list(scheduler, only_node);
128 0 : resources = pe__build_rsc_list(scheduler, only_rsc);
129 :
130 : /* Always print DC if NULL. */
131 0 : if (scheduler->dc_node == NULL) {
132 0 : show |= pcmk_section_dc;
133 : }
134 :
135 0 : if (simple_output) {
136 0 : rc = pcmk__output_simple_status(out, scheduler);
137 : } else {
138 0 : out->message(out, "cluster-status",
139 0 : scheduler, pcmkd_state, pcmk_rc2exitc(history_rc),
140 : stonith_history, fence_history, show, show_opts,
141 : neg_location_prefix, unames, resources);
142 : }
143 :
144 0 : g_list_free_full(unames, free);
145 0 : g_list_free_full(resources, free);
146 :
147 0 : stonith_history_free(stonith_history);
148 0 : stonith_history = NULL;
149 0 : pe_free_working_set(scheduler);
150 0 : return rc;
151 : }
152 :
153 : int
154 0 : pcmk_status(xmlNodePtr *xml)
155 : {
156 0 : cib_t *cib = NULL;
157 0 : pcmk__output_t *out = NULL;
158 0 : int rc = pcmk_rc_ok;
159 :
160 0 : uint32_t show_opts = pcmk_show_pending
161 : |pcmk_show_inactive_rscs
162 : |pcmk_show_timing;
163 :
164 0 : cib = cib_new();
165 :
166 0 : if (cib == NULL) {
167 0 : return pcmk_rc_cib_corrupt;
168 : }
169 :
170 0 : rc = pcmk__xml_output_new(&out, xml);
171 0 : if (rc != pcmk_rc_ok) {
172 0 : cib_delete(cib);
173 0 : return rc;
174 : }
175 :
176 0 : pcmk__register_lib_messages(out);
177 0 : pe__register_messages(out);
178 0 : stonith__register_messages(out);
179 :
180 0 : rc = pcmk__status(out, cib, pcmk__fence_history_full, pcmk_section_all,
181 : show_opts, NULL, NULL, NULL, false, 0);
182 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
183 :
184 0 : cib_delete(cib);
185 0 : return rc;
186 : }
187 :
188 : /*!
189 : * \internal
190 : * \brief Query and output the cluster status
191 : *
192 : * The operation is considered a success if we're able to get the \p pacemakerd
193 : * state. If possible, we'll also try to connect to the fencer and CIB and
194 : * output their respective status information.
195 : *
196 : * \param[in,out] out Output object
197 : * \param[in,out] cib CIB connection
198 : * \param[in] fence_history How much of the fencing history to output
199 : * \param[in] show Group of \p pcmk_section_e flags
200 : * \param[in] show_opts Group of \p pcmk_show_opt_e flags
201 : * \param[in] only_node If a node name or tag, include only the
202 : * matching node(s) (if any) in the output.
203 : * If \p "*" or \p NULL, include all nodes
204 : * in the output.
205 : * \param[in] only_rsc If a resource ID or tag, include only the
206 : * matching resource(s) (if any) in the
207 : * output. If \p "*" or \p NULL, include all
208 : * resources in the output.
209 : * \param[in] neg_location_prefix Prefix denoting a ban in a constraint ID
210 : * \param[in] simple_output Whether to use a simple output format.
211 : * Note: This is for use by \p crm_mon only
212 : * and is planned to be deprecated.
213 : * \param[in] timeout_ms How long to wait for a reply from the
214 : * \p pacemakerd API. If 0,
215 : * \p pcmk_ipc_dispatch_sync will be used.
216 : * If positive, \p pcmk_ipc_dispatch_main
217 : * will be used, and a new mainloop will be
218 : * created for this purpose (freed before
219 : * return).
220 : *
221 : * \return Standard Pacemaker return code
222 : */
223 : int
224 0 : pcmk__status(pcmk__output_t *out, cib_t *cib,
225 : enum pcmk__fence_history fence_history, uint32_t show,
226 : uint32_t show_opts, const char *only_node, const char *only_rsc,
227 : const char *neg_location_prefix, bool simple_output,
228 : unsigned int timeout_ms)
229 : {
230 0 : xmlNode *current_cib = NULL;
231 0 : int rc = pcmk_rc_ok;
232 0 : stonith_t *stonith = NULL;
233 0 : enum pcmk_pacemakerd_state pcmkd_state = pcmk_pacemakerd_state_invalid;
234 0 : time_t last_updated = 0;
235 :
236 0 : if (cib == NULL) {
237 0 : return ENOTCONN;
238 : }
239 :
240 0 : if (cib->variant == cib_native) {
241 0 : rc = pcmk__pacemakerd_status(out, crm_system_name, timeout_ms, false,
242 : &pcmkd_state);
243 0 : if (rc != pcmk_rc_ok) {
244 0 : return rc;
245 : }
246 :
247 0 : last_updated = time(NULL);
248 :
249 0 : switch (pcmkd_state) {
250 0 : case pcmk_pacemakerd_state_running:
251 : case pcmk_pacemakerd_state_shutting_down:
252 : case pcmk_pacemakerd_state_remote:
253 : /* Fencer and CIB may still be available while shutting down or
254 : * running on a Pacemaker Remote node
255 : */
256 0 : break;
257 0 : default:
258 : // Fencer and CIB are definitely unavailable
259 0 : out->message(out, "pacemakerd-health",
260 : NULL, pcmkd_state, NULL, last_updated);
261 0 : return rc;
262 : }
263 :
264 0 : if (fence_history != pcmk__fence_history_none) {
265 0 : stonith = fencing_connect();
266 : }
267 : }
268 :
269 0 : rc = cib__signon_query(out, &cib, ¤t_cib);
270 0 : if (rc != pcmk_rc_ok) {
271 0 : if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
272 : // Invalid at this point means we didn't query the pcmkd state
273 0 : out->message(out, "pacemakerd-health",
274 : NULL, pcmkd_state, NULL, last_updated);
275 : }
276 0 : goto done;
277 : }
278 :
279 0 : rc = pcmk__output_cluster_status(out, stonith, cib, current_cib,
280 : pcmkd_state, fence_history, show,
281 : show_opts, only_node, only_rsc,
282 : neg_location_prefix, simple_output);
283 0 : if (rc != pcmk_rc_ok) {
284 0 : out->err(out, "Error outputting status info from the fencer or CIB");
285 : }
286 :
287 0 : done:
288 0 : stonith_api_delete(stonith);
289 0 : free_xml(current_cib);
290 0 : return pcmk_rc_ok;
291 : }
292 :
293 : /*!
294 : * \internal
295 : * \brief Output cluster status in Nagios Plugin format
296 : *
297 : * \param[in,out] out Output object
298 : * \param[in] scheduler Scheduler data
299 : *
300 : * \return Standard Pacemaker return code
301 : * \note This is for a deprecated crm_mon option and should be called only for
302 : * that.
303 : */
304 : int
305 0 : pcmk__output_simple_status(pcmk__output_t *out,
306 : const pcmk_scheduler_t *scheduler)
307 : {
308 0 : int nodes_online = 0;
309 0 : int nodes_standby = 0;
310 0 : int nodes_maint = 0;
311 0 : GString *offline_nodes = NULL;
312 0 : bool no_dc = false;
313 0 : bool offline = false;
314 0 : bool has_warnings = false;
315 :
316 0 : if (scheduler->dc_node == NULL) {
317 0 : has_warnings = true;
318 0 : no_dc = true;
319 : }
320 :
321 0 : for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
322 0 : pcmk_node_t *node = (pcmk_node_t *) iter->data;
323 :
324 0 : if (node->details->standby && node->details->online) {
325 0 : nodes_standby++;
326 0 : } else if (node->details->maintenance && node->details->online) {
327 0 : nodes_maint++;
328 0 : } else if (node->details->online) {
329 0 : nodes_online++;
330 : } else {
331 0 : pcmk__add_word(&offline_nodes, 1024, "offline node:");
332 0 : pcmk__add_word(&offline_nodes, 0, pcmk__node_name(node));
333 0 : has_warnings = true;
334 0 : offline = true;
335 : }
336 : }
337 :
338 0 : if (has_warnings) {
339 0 : out->info(out, "CLUSTER WARN: %s%s%s",
340 : no_dc ? "No DC" : "",
341 0 : no_dc && offline ? ", " : "",
342 0 : (offline? (const char *) offline_nodes->str : ""));
343 :
344 0 : if (offline_nodes != NULL) {
345 0 : g_string_free(offline_nodes, TRUE);
346 : }
347 :
348 : } else {
349 0 : char *nodes_standby_s = NULL;
350 0 : char *nodes_maint_s = NULL;
351 :
352 0 : if (nodes_standby > 0) {
353 0 : nodes_standby_s = crm_strdup_printf(", %d standby node%s",
354 : nodes_standby,
355 : pcmk__plural_s(nodes_standby));
356 : }
357 :
358 0 : if (nodes_maint > 0) {
359 0 : nodes_maint_s = crm_strdup_printf(", %d maintenance node%s",
360 : nodes_maint,
361 : pcmk__plural_s(nodes_maint));
362 : }
363 :
364 0 : out->info(out, "CLUSTER OK: %d node%s online%s%s, "
365 : "%d resource instance%s configured",
366 : nodes_online, pcmk__plural_s(nodes_online),
367 : nodes_standby_s != NULL ? nodes_standby_s : "",
368 : nodes_maint_s != NULL ? nodes_maint_s : "",
369 0 : scheduler->ninstances, pcmk__plural_s(scheduler->ninstances));
370 :
371 0 : free(nodes_standby_s);
372 0 : free(nodes_maint_s);
373 : }
374 :
375 0 : if (has_warnings) {
376 0 : return pcmk_rc_error;
377 : } else {
378 0 : return pcmk_rc_ok;
379 : }
380 : /* coverity[leaked_storage] False positive */
381 : }
|