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 Lesser General Public License
7 : * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 : */
9 :
10 : #include <crm_internal.h>
11 :
12 : #include <crm/crm.h>
13 : #include <crm/common/xml.h>
14 : #include <pacemaker-internal.h>
15 :
16 : /*!
17 : * \internal
18 : * \brief Return text equivalent of an enum pcmk__graph_status for logging
19 : *
20 : * \param[in] state Transition status
21 : *
22 : * \return Human-readable text equivalent of \p state
23 : */
24 : const char *
25 0 : pcmk__graph_status2text(enum pcmk__graph_status state)
26 : {
27 0 : switch (state) {
28 0 : case pcmk__graph_active:
29 0 : return "active";
30 0 : case pcmk__graph_pending:
31 0 : return "pending";
32 0 : case pcmk__graph_complete:
33 0 : return "complete";
34 0 : case pcmk__graph_terminated:
35 0 : return "terminated";
36 : }
37 0 : return "unknown";
38 : }
39 :
40 : static const char *
41 0 : actiontype2text(enum pcmk__graph_action_type type)
42 : {
43 0 : switch (type) {
44 0 : case pcmk__pseudo_graph_action:
45 0 : return "pseudo";
46 0 : case pcmk__rsc_graph_action:
47 0 : return "resource";
48 0 : case pcmk__cluster_graph_action:
49 0 : return "cluster";
50 : }
51 0 : return "invalid";
52 : }
53 :
54 : /*!
55 : * \internal
56 : * \brief Find a transition graph action by ID
57 : *
58 : * \param[in] graph Transition graph to search
59 : * \param[in] id Action ID to search for
60 : *
61 : * \return Transition graph action corresponding to \p id, or NULL if none
62 : */
63 : static const pcmk__graph_action_t *
64 0 : find_graph_action_by_id(const pcmk__graph_t *graph, int id)
65 : {
66 0 : if (graph == NULL) {
67 0 : return NULL;
68 : }
69 :
70 0 : for (const GList *synapse_iter = graph->synapses;
71 0 : synapse_iter != NULL; synapse_iter = synapse_iter->next) {
72 :
73 0 : const pcmk__graph_synapse_t *synapse = synapse_iter->data;
74 :
75 0 : for (const GList *action_iter = synapse->actions;
76 0 : action_iter != NULL; action_iter = action_iter->next) {
77 :
78 0 : const pcmk__graph_action_t *action = action_iter->data;
79 0 : if (action->id == id) {
80 0 : return action;
81 : }
82 : }
83 : }
84 0 : return NULL;
85 : }
86 :
87 : static const char *
88 0 : synapse_state_str(pcmk__graph_synapse_t *synapse)
89 : {
90 0 : if (pcmk_is_set(synapse->flags, pcmk__synapse_failed)) {
91 0 : return "Failed";
92 :
93 0 : } else if (pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
94 0 : return "Completed";
95 :
96 0 : } else if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
97 0 : return "In-flight";
98 :
99 0 : } else if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
100 0 : return "Ready";
101 : }
102 0 : return "Pending";
103 : }
104 :
105 : /*!
106 : * \internal
107 : * \brief List the action IDs of pending inputs to a transition graph synapse
108 : *
109 : * \param[in] graph Transition graph to which \p synapse belongs
110 : * \param[in] synapse Synapse whose inputs to check
111 : *
112 : * \return A \p GString containing the space-delimited action IDs of inputs to
113 : * \p synapse that haven't completed successfully
114 : *
115 : * \note The caller is responsible for freeing the return value using
116 : * \p g_string_free().
117 : */
118 : static GString *
119 0 : synapse_pending_inputs(const pcmk__graph_t *graph,
120 : const pcmk__graph_synapse_t *synapse)
121 : {
122 0 : GString *pending = NULL;
123 :
124 0 : for (const GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
125 0 : const pcmk__graph_action_t *input = (pcmk__graph_action_t *) lpc->data;
126 :
127 0 : if (pcmk_is_set(input->flags, pcmk__graph_action_failed)) {
128 0 : pcmk__add_word(&pending, 1024, pcmk__xe_id(input->xml));
129 :
130 0 : } else if (pcmk_is_set(input->flags, pcmk__graph_action_confirmed)) {
131 : // Confirmed successful inputs are not pending
132 :
133 0 : } else if (find_graph_action_by_id(graph, input->id) != NULL) {
134 : // In-flight or pending
135 0 : pcmk__add_word(&pending, 1024, pcmk__xe_id(input->xml));
136 : }
137 : }
138 0 : return pending;
139 : }
140 :
141 : // Log synapse inputs that aren't in graph
142 : static void
143 0 : log_unresolved_inputs(unsigned int log_level, pcmk__graph_t *graph,
144 : pcmk__graph_synapse_t *synapse)
145 : {
146 0 : for (GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
147 0 : pcmk__graph_action_t *input = (pcmk__graph_action_t *) lpc->data;
148 0 : const char *key = crm_element_value(input->xml, PCMK__XA_OPERATION_KEY);
149 0 : const char *host = crm_element_value(input->xml, PCMK__META_ON_NODE);
150 :
151 0 : if (find_graph_action_by_id(graph, input->id) == NULL) {
152 0 : do_crm_log(log_level,
153 : " * [Input %2d]: Unresolved dependency %s op %s%s%s",
154 : input->id, actiontype2text(input->type), key,
155 : (host? " on " : ""), (host? host : ""));
156 : }
157 : }
158 0 : }
159 :
160 : static void
161 0 : log_synapse_action(unsigned int log_level, pcmk__graph_synapse_t *synapse,
162 : pcmk__graph_action_t *action, const char *pending_inputs)
163 : {
164 0 : const char *key = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
165 0 : const char *host = crm_element_value(action->xml, PCMK__META_ON_NODE);
166 0 : char *desc = crm_strdup_printf("%s %s op %s",
167 : synapse_state_str(synapse),
168 : actiontype2text(action->type), key);
169 :
170 0 : do_crm_log(log_level,
171 : "[Action %4d]: %-50s%s%s (priority: %d, waiting: %s)",
172 : action->id, desc, (host? " on " : ""), (host? host : ""),
173 : synapse->priority, pending_inputs);
174 0 : free(desc);
175 0 : }
176 :
177 : static void
178 0 : log_synapse(unsigned int log_level, pcmk__graph_t *graph,
179 : pcmk__graph_synapse_t *synapse)
180 : {
181 0 : GString *g_pending = NULL;
182 0 : const char *pending = "none";
183 :
184 0 : if (!pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
185 0 : g_pending = synapse_pending_inputs(graph, synapse);
186 :
187 0 : if (g_pending != NULL) {
188 0 : pending = (const char *) g_pending->str;
189 : }
190 : }
191 :
192 0 : for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
193 0 : log_synapse_action(log_level, synapse,
194 0 : (pcmk__graph_action_t *) lpc->data, pending);
195 : }
196 :
197 0 : if (g_pending != NULL) {
198 0 : g_string_free(g_pending, TRUE);
199 : }
200 :
201 0 : if (!pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
202 0 : log_unresolved_inputs(log_level, graph, synapse);
203 : }
204 0 : }
205 :
206 : void
207 0 : pcmk__log_graph_action(int log_level, pcmk__graph_action_t *action)
208 : {
209 0 : log_synapse(log_level, NULL, action->synapse);
210 0 : }
211 :
212 : void
213 0 : pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph)
214 : {
215 0 : if ((graph == NULL) || (graph->num_actions == 0)) {
216 0 : if (log_level == LOG_TRACE) {
217 0 : crm_debug("Empty transition graph");
218 : }
219 0 : return;
220 : }
221 :
222 0 : do_crm_log(log_level,
223 : "Graph %d with %d actions: " PCMK_OPT_BATCH_LIMIT "=%d jobs, "
224 : "network-delay=%ums",
225 : graph->id, graph->num_actions,
226 : graph->batch_limit, graph->network_delay);
227 :
228 0 : for (GList *lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
229 0 : log_synapse(log_level, graph, (pcmk__graph_synapse_t *) lpc->data);
230 : }
231 : }
|