1 /*
2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include <crm_internal.h>
20 
21 #include <crm/crm.h>
22 #include <crm/msg_xml.h>
23 #include <crm/common/xml.h>
24 #include <crm/transition.h>
25 /* #include <sys/param.h> */
26 /*  */
27 
28 crm_graph_functions_t *graph_fns = NULL;
29 
30 static gboolean
update_synapse_ready(synapse_t * synapse,int action_id)31 update_synapse_ready(synapse_t * synapse, int action_id)
32 {
33     GListPtr lpc = NULL;
34     gboolean updates = FALSE;
35 
36     CRM_CHECK(synapse->executed == FALSE, return FALSE);
37     CRM_CHECK(synapse->confirmed == FALSE, return FALSE);
38 
39     synapse->ready = TRUE;
40     for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
41         crm_action_t *prereq = (crm_action_t *) lpc->data;
42 
43         crm_trace("Processing input %d", prereq->id);
44 
45         if (prereq->id == action_id) {
46             crm_trace("Marking input %d of synapse %d confirmed", action_id, synapse->id);
47             prereq->confirmed = TRUE;
48             updates = TRUE;
49 
50         } else if (prereq->confirmed == FALSE) {
51             synapse->ready = FALSE;
52         }
53 
54     }
55 
56     if (updates) {
57         crm_trace("Updated synapse %d", synapse->id);
58     }
59     return updates;
60 }
61 
62 static gboolean
update_synapse_confirmed(synapse_t * synapse,int action_id)63 update_synapse_confirmed(synapse_t * synapse, int action_id)
64 {
65     GListPtr lpc = NULL;
66     gboolean updates = FALSE;
67     gboolean is_confirmed = TRUE;
68 
69     CRM_CHECK(synapse->executed, return FALSE);
70     CRM_CHECK(synapse->confirmed == FALSE, return TRUE);
71 
72     is_confirmed = TRUE;
73     for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
74         crm_action_t *action = (crm_action_t *) lpc->data;
75 
76         crm_trace("Processing action %d", action->id);
77 
78         if (action->id == action_id) {
79             crm_trace("Confirmed: Action %d of Synapse %d", action_id, synapse->id);
80             action->confirmed = TRUE;
81             updates = TRUE;
82 
83         } else if (action->confirmed == FALSE) {
84             is_confirmed = FALSE;
85             crm_trace("Synapse %d still not confirmed after action %d", synapse->id, action_id);
86         }
87     }
88 
89     if (is_confirmed && synapse->confirmed == FALSE) {
90         crm_trace("Confirmed: Synapse %d", synapse->id);
91         synapse->confirmed = TRUE;
92         updates = TRUE;
93     }
94 
95     if (updates) {
96         crm_trace("Updated synapse %d", synapse->id);
97     }
98     return updates;
99 }
100 
101 gboolean
update_graph(crm_graph_t * graph,crm_action_t * action)102 update_graph(crm_graph_t * graph, crm_action_t * action)
103 {
104     gboolean rc = FALSE;
105     gboolean updates = FALSE;
106     GListPtr lpc = NULL;
107 
108     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
109         synapse_t *synapse = (synapse_t *) lpc->data;
110 
111         if (synapse->confirmed || synapse->failed) {
112             crm_trace("Synapse complete");
113 
114         } else if (synapse->executed) {
115             crm_trace("Synapse executed");
116             rc = update_synapse_confirmed(synapse, action->id);
117 
118         } else if (action->failed == FALSE || synapse->priority == INFINITY) {
119             rc = update_synapse_ready(synapse, action->id);
120         }
121         updates = updates || rc;
122     }
123 
124     if (updates) {
125         crm_trace("Updated graph with completed action %d", action->id);
126     }
127     return updates;
128 }
129 
130 static gboolean
should_fire_synapse(crm_graph_t * graph,synapse_t * synapse)131 should_fire_synapse(crm_graph_t * graph, synapse_t * synapse)
132 {
133     GListPtr lpc = NULL;
134 
135     CRM_CHECK(synapse->executed == FALSE, return FALSE);
136     CRM_CHECK(synapse->confirmed == FALSE, return FALSE);
137 
138     crm_trace("Checking pre-reqs for synapse %d", synapse->id);
139     /* lookup prereqs */
140     synapse->ready = TRUE;
141     for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
142         crm_action_t *prereq = (crm_action_t *) lpc->data;
143 
144         crm_trace("Processing input %d", prereq->id);
145         if (prereq->confirmed == FALSE) {
146             crm_trace("Input %d for synapse %d not satisfied: not confirmed", prereq->id, synapse->id);
147             synapse->ready = FALSE;
148             break;
149         } else if(prereq->failed && prereq->can_fail == FALSE) {
150             crm_trace("Input %d for synapse %d not satisfied: failed", prereq->id, synapse->id);
151             synapse->ready = FALSE;
152             break;
153         }
154     }
155 
156     for (lpc = synapse->actions; synapse->ready && lpc != NULL; lpc = lpc->next) {
157         crm_action_t *a = (crm_action_t *) lpc->data;
158 
159         if (a->type == action_type_pseudo) {
160             /* None of the below applies to pseudo ops */
161 
162         } else if (synapse->priority < graph->abort_priority) {
163             crm_trace("Skipping synapse %d: abort level %d", synapse->id, graph->abort_priority);
164             graph->skipped++;
165             return FALSE;
166 
167         } else if(graph_fns->allowed && graph_fns->allowed(graph, a) == FALSE) {
168             crm_trace("Deferring synapse %d: allowed", synapse->id);
169             return FALSE;
170         }
171     }
172 
173     return synapse->ready;
174 }
175 
176 static gboolean
initiate_action(crm_graph_t * graph,crm_action_t * action)177 initiate_action(crm_graph_t * graph, crm_action_t * action)
178 {
179     const char *id = NULL;
180 
181     CRM_CHECK(action->executed == FALSE, return FALSE);
182 
183     id = ID(action->xml);
184     CRM_CHECK(id != NULL, return FALSE);
185 
186     action->executed = TRUE;
187     if (action->type == action_type_pseudo) {
188         crm_trace("Executing pseudo-event: %s (%d)", id, action->id);
189         return graph_fns->pseudo(graph, action);
190 
191     } else if (action->type == action_type_rsc) {
192         crm_trace("Executing rsc-event: %s (%d)", id, action->id);
193         return graph_fns->rsc(graph, action);
194 
195     } else if (action->type == action_type_crm) {
196         const char *task = NULL;
197 
198         task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
199         CRM_CHECK(task != NULL, return FALSE);
200 
201         if (safe_str_eq(task, CRM_OP_FENCE)) {
202             crm_trace("Executing STONITH-event: %s (%d)", id, action->id);
203             return graph_fns->stonith(graph, action);
204         }
205 
206         crm_trace("Executing crm-event: %s (%d)", id, action->id);
207         return graph_fns->crmd(graph, action);
208     }
209 
210     crm_err("Failed on unsupported command type: %s (id=%s)", crm_element_name(action->xml), id);
211     return FALSE;
212 }
213 
214 static gboolean
fire_synapse(crm_graph_t * graph,synapse_t * synapse)215 fire_synapse(crm_graph_t * graph, synapse_t * synapse)
216 {
217     GListPtr lpc = NULL;
218 
219     CRM_CHECK(synapse != NULL, return FALSE);
220     CRM_CHECK(synapse->ready, return FALSE);
221     CRM_CHECK(synapse->confirmed == FALSE, return TRUE);
222 
223     crm_trace("Synapse %d fired", synapse->id);
224     synapse->executed = TRUE;
225     for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
226         crm_action_t *action = (crm_action_t *) lpc->data;
227 
228         /* allow some leeway */
229         gboolean passed = FALSE;
230 
231         /* Invoke the action and start the timer */
232         passed = initiate_action(graph, action);
233         if (passed == FALSE) {
234             crm_err("Failed initiating <%s id=%d> in synapse %d",
235                     crm_element_name(action->xml), action->id, synapse->id);
236             synapse->confirmed = TRUE;
237             action->confirmed = TRUE;
238             action->failed = TRUE;
239             return FALSE;
240         }
241     }
242 
243     return TRUE;
244 }
245 
246 int
run_graph(crm_graph_t * graph)247 run_graph(crm_graph_t * graph)
248 {
249     GListPtr lpc = NULL;
250     int stat_log_level = LOG_DEBUG;
251     int pass_result = transition_active;
252 
253     const char *status = "In-progress";
254 
255     if (graph_fns == NULL) {
256         set_default_graph_functions();
257     }
258     if (graph == NULL) {
259         return transition_complete;
260     }
261 
262     graph->fired = 0;
263     graph->pending = 0;
264     graph->skipped = 0;
265     graph->completed = 0;
266     graph->incomplete = 0;
267     crm_trace("Entering graph %d callback", graph->id);
268 
269     /* Pre-calculate the number of completed and in-flight operations */
270     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
271         synapse_t *synapse = (synapse_t *) lpc->data;
272 
273         if (synapse->confirmed) {
274             crm_trace("Synapse %d complete", synapse->id);
275             graph->completed++;
276 
277         } else if (synapse->failed == FALSE && synapse->executed) {
278             crm_trace("Synapse %d: confirmation pending", synapse->id);
279             graph->pending++;
280         }
281     }
282 
283     /* Now check if there is work to do */
284     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
285         synapse_t *synapse = (synapse_t *) lpc->data;
286 
287         if (graph->batch_limit > 0 && graph->pending >= graph->batch_limit) {
288             crm_debug("Throttling output: batch limit (%d) reached", graph->batch_limit);
289             break;
290         } else if (synapse->failed) {
291             graph->skipped++;
292             continue;
293 
294         } else if (synapse->confirmed || synapse->executed) {
295             /* Already handled */
296             continue;
297         }
298 
299         if (should_fire_synapse(graph, synapse)) {
300             crm_trace("Synapse %d fired", synapse->id);
301             graph->fired++;
302             if(fire_synapse(graph, synapse) == FALSE) {
303                 crm_err("Synapse %d failed to fire", synapse->id);
304                 stat_log_level = LOG_ERR;
305                 graph->abort_priority = INFINITY;
306                 graph->incomplete++;
307                 graph->fired--;
308             }
309 
310             if (synapse->confirmed == FALSE) {
311                 graph->pending++;
312             }
313 
314         } else {
315             crm_trace("Synapse %d cannot fire", synapse->id);
316             graph->incomplete++;
317         }
318     }
319 
320     if (graph->pending == 0 && graph->fired == 0) {
321         graph->complete = TRUE;
322         stat_log_level = LOG_NOTICE;
323         pass_result = transition_complete;
324         status = "Complete";
325 
326         if (graph->incomplete != 0 && graph->abort_priority <= 0) {
327             stat_log_level = LOG_WARNING;
328             pass_result = transition_terminated;
329             status = "Terminated";
330 
331         } else if (graph->skipped != 0) {
332             status = "Stopped";
333         }
334 
335     } else if (graph->fired == 0) {
336         pass_result = transition_pending;
337     }
338 
339     do_crm_log(stat_log_level,
340                "Transition %d (Complete=%d, Pending=%d,"
341                " Fired=%d, Skipped=%d, Incomplete=%d, Source=%s): %s",
342                graph->id, graph->completed, graph->pending, graph->fired,
343                graph->skipped, graph->incomplete, graph->source, status);
344 
345     return pass_result;
346 }
347