1 /*
2 * Copyright 2004-2023 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 <sys/param.h>
13 #include <sys/stat.h>
14
15 #include <crm/crm.h>
16 #include <crm/msg_xml.h>
17 #include <crm/common/xml.h>
18 #include <crm/common/xml_internal.h>
19 #include <crm/lrmd_internal.h>
20 #include <pacemaker-internal.h>
21
22
23 /*
24 * Functions for updating graph
25 */
26
27 /*!
28 * \internal
29 * \brief Update synapse after completed prerequisite
30 *
31 * A synapse is ready to be executed once all its prerequisite actions (inputs)
32 * complete. Given a completed action, check whether it is an input for a given
33 * synapse, and if so, mark the input as confirmed, and mark the synapse as
34 * ready if appropriate.
35 *
36 * \param[in,out] synapse Transition graph synapse to update
37 * \param[in] action_id ID of an action that completed
38 *
39 * \note The only substantial effect here is confirming synapse inputs.
40 * should_fire_synapse() will recalculate pcmk__synapse_ready, so the only
41 * thing that uses the pcmk__synapse_ready from here is
42 * synapse_state_str().
43 */
44 static void
45 update_synapse_ready(pcmk__graph_synapse_t *synapse, int action_id)
46 {
47 if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
48 return; // All inputs have already been confirmed
49 }
50
51 // Presume ready until proven otherwise
52 pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
53
54 for (GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
55 pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
56
57 if (prereq->id == action_id) {
58 crm_trace("Confirming input %d of synapse %d",
59 action_id, synapse->id);
60 pcmk__set_graph_action_flags(prereq, pcmk__graph_action_confirmed);
61
62 } else if (!pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed)) {
63 pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
64 crm_trace("Synapse %d still not ready after action %d",
65 synapse->id, action_id);
66 }
67 }
68 if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
69 crm_trace("Synapse %d is now ready to execute", synapse->id);
70 }
71 }
72
73 /*!
74 * \internal
75 * \brief Update action and synapse confirmation after action completion
76 *
77 * \param[in,out] synapse Transition graph synapse that action belongs to
78 * \param[in] action_id ID of action that completed
79 */
80 static void
81 update_synapse_confirmed(pcmk__graph_synapse_t *synapse, int action_id)
82 {
83 bool all_confirmed = true;
84
85 for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
86 pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
87
88 if (action->id == action_id) {
89 crm_trace("Confirmed action %d of synapse %d",
90 action_id, synapse->id);
91 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
92
93 } else if (all_confirmed &&
94 !pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
95 all_confirmed = false;
96 crm_trace("Synapse %d still not confirmed after action %d",
97 synapse->id, action_id);
98 }
99 }
100
101 if (all_confirmed
102 && !pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
103 crm_trace("Confirmed synapse %d", synapse->id);
104 pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
105 }
106 }
107
108 /*!
109 * \internal
110 * \brief Update the transition graph with a completed action result
111 *
112 * \param[in,out] graph Transition graph to update
113 * \param[in] action Action that completed
114 */
115 void
116 pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action)
117 {
118 for (GList *lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
119 pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
120
121 if (pcmk_any_flags_set(synapse->flags,
122 pcmk__synapse_confirmed|pcmk__synapse_failed)) {
123 continue; // This synapse already completed
124
125 } else if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
126 update_synapse_confirmed(synapse, action->id);
127
128 } else if (!pcmk_is_set(action->flags, pcmk__graph_action_failed)
129 || (synapse->priority == INFINITY)) {
130 update_synapse_ready(synapse, action->id);
131 }
132 }
133 }
134
135
136 /*
137 * Functions for executing graph
138 */
139
140 /* A transition graph consists of various types of actions. The library caller
141 * registers execution functions for each action type, which will be stored
142 * here.
143 */
144 static pcmk__graph_functions_t *graph_fns = NULL;
145
146 /*!
147 * \internal
148 * \brief Set transition graph execution functions
149 *
150 * \param[in] Execution functions to use
151 */
152 void
153 pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
154 {
155 crm_debug("Setting custom functions for executing transition graphs");
156 graph_fns = fns;
157
158 CRM_ASSERT(graph_fns != NULL);
159 CRM_ASSERT(graph_fns->rsc != NULL);
160 CRM_ASSERT(graph_fns->cluster != NULL);
161 CRM_ASSERT(graph_fns->pseudo != NULL);
162 CRM_ASSERT(graph_fns->fence != NULL);
163 }
164
165 /*!
166 * \internal
167 * \brief Check whether a graph synapse is ready to be executed
168 *
169 * \param[in,out] graph Transition graph that synapse is part of
170 * \param[in,out] synapse Synapse to check
171 *
172 * \return true if synapse is ready, false otherwise
173 */
174 static bool
175 should_fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
176 {
177 GList *lpc = NULL;
178
179 pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
180 for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
181 pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
182
183 if (!(pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed))) {
184 crm_trace("Input %d for synapse %d not yet confirmed",
185 prereq->id, synapse->id);
186 pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
187 break;
188
189 } else if (pcmk_is_set(prereq->flags, pcmk__graph_action_failed)
190 && !pcmk_is_set(prereq->flags,
191 pcmk__graph_action_can_fail)) {
192 crm_trace("Input %d for synapse %d confirmed but failed",
193 prereq->id, synapse->id);
194 pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
195 break;
196 }
197 }
198 if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
199 crm_trace("Synapse %d is ready to execute", synapse->id);
200 } else {
201 return false;
202 }
203
204 for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
205 pcmk__graph_action_t *a = (pcmk__graph_action_t *) lpc->data;
206
207 if (a->type == pcmk__pseudo_graph_action) {
208 /* None of the below applies to pseudo ops */
209
210 } else if (synapse->priority < graph->abort_priority) {
211 crm_trace("Skipping synapse %d: priority %d is less than "
212 "abort priority %d",
213 synapse->id, synapse->priority, graph->abort_priority);
214 graph->skipped++;
215 return false;
216
217 } else if (graph_fns->allowed && !(graph_fns->allowed(graph, a))) {
218 crm_trace("Deferring synapse %d: not allowed", synapse->id);
219 return false;
220 }
221 }
222
223 return true;
224 }
225
226 /*!
227 * \internal
228 * \brief Initiate an action from a transition graph
229 *
230 * \param[in,out] graph Transition graph containing action
231 * \param[in,out] action Action to execute
232 *
233 * \return Standard Pacemaker return code
234 */
235 static int
236 initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
237 {
238 const char *id = ID(action->xml);
239
240 CRM_CHECK(id != NULL, return EINVAL);
241 CRM_CHECK(!pcmk_is_set(action->flags, pcmk__graph_action_executed),
242 return pcmk_rc_already);
243
244 pcmk__set_graph_action_flags(action, pcmk__graph_action_executed);
245 switch (action->type) {
246 case pcmk__pseudo_graph_action:
247 crm_trace("Executing pseudo-action %d (%s)", action->id, id);
248 return graph_fns->pseudo(graph, action);
249
250 case pcmk__rsc_graph_action:
251 crm_trace("Executing resource action %d (%s)", action->id, id);
252 return graph_fns->rsc(graph, action);
253
254 case pcmk__cluster_graph_action:
255 if (pcmk__str_eq(crm_element_value(action->xml, XML_LRM_ATTR_TASK),
256 PCMK_ACTION_STONITH, pcmk__str_none)) {
257 crm_trace("Executing fencing action %d (%s)",
258 action->id, id);
259 return graph_fns->fence(graph, action);
260 }
261 crm_trace("Executing cluster action %d (%s)", action->id, id);
262 return graph_fns->cluster(graph, action);
263
264 default:
265 crm_err("Unsupported graph action type <%s " XML_ATTR_ID "='%s'> "
266 "(bug?)",
267 action->xml->name, id);
268 return EINVAL;
269 }
270 }
271
272 /*!
273 * \internal
274 * \brief Execute a graph synapse
275 *
276 * \param[in,out] graph Transition graph with synapse to execute
277 * \param[in,out] synapse Synapse to execute
278 *
279 * \return Standard Pacemaker return value
280 */
281 static int
282 fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
283 {
284 pcmk__set_synapse_flags(synapse, pcmk__synapse_executed);
285 for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
286 pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
287 int rc = initiate_action(graph, action);
288
289 if (rc != pcmk_rc_ok) {
290 crm_err("Failed initiating <%s " XML_ATTR_ID "=%d> in synapse %d: "
291 "%s",
292 action->xml->name, action->id, synapse->id,
293 pcmk_rc_str(rc));
294 pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
295 pcmk__set_graph_action_flags(action,
296 pcmk__graph_action_confirmed
297 |pcmk__graph_action_failed);
298 return pcmk_rc_error;
299 }
300 }
301 return pcmk_rc_ok;
302 }
303
304 /*!
305 * \internal
306 * \brief Dummy graph method that can be used with simulations
307 *
308 * \param[in,out] graph Transition graph containing action
309 * \param[in,out] action Graph action to be initiated
310 *
311 * \return Standard Pacemaker return code
312 * \note If the PE_fail environment variable is set to the action ID,
313 * then the graph action will be marked as failed.
314 */
315 static int
316 pseudo_action_dummy(pcmk__graph_t *graph, pcmk__graph_action_t *action)
317 {
318 static int fail = -1;
319
320 if (fail < 0) {
321 long long fail_ll;
322
323 if ((pcmk__scan_ll(getenv("PE_fail"), &fail_ll, 0LL) == pcmk_rc_ok)
324 && (fail_ll > 0LL) && (fail_ll <= INT_MAX)) {
325 fail = (int) fail_ll;
326 } else {
327 fail = 0;
328 }
329 }
330
331 if (action->id == fail) {
332 crm_err("Dummy event handler: pretending action %d failed", action->id);
333 pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
334 graph->abort_priority = INFINITY;
335 } else {
336 crm_trace("Dummy event handler: action %d initiated", action->id);
337 }
338 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
339 pcmk__update_graph(graph, action);
340 return pcmk_rc_ok;
341 }
342
343 static pcmk__graph_functions_t default_fns = {
344 pseudo_action_dummy,
345 pseudo_action_dummy,
346 pseudo_action_dummy,
347 pseudo_action_dummy
348 };
349
350 /*!
351 * \internal
352 * \brief Execute all actions in a transition graph
353 *
354 * \param[in,out] graph Transition graph to execute
355 *
356 * \return Status of transition after execution
357 */
358 enum pcmk__graph_status
359 pcmk__execute_graph(pcmk__graph_t *graph)
360 {
361 GList *lpc = NULL;
362 int log_level = LOG_DEBUG;
363 enum pcmk__graph_status pass_result = pcmk__graph_active;
364 const char *status = "In progress";
365
366 if (graph_fns == NULL) {
367 graph_fns = &default_fns;
368 }
369 if (graph == NULL) {
370 return pcmk__graph_complete;
371 }
372
373 graph->fired = 0;
374 graph->pending = 0;
375 graph->skipped = 0;
376 graph->completed = 0;
377 graph->incomplete = 0;
378
379 // Count completed and in-flight synapses
380 for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
381 pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
382
383 if (pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
384 graph->completed++;
385
386 } else if (!pcmk_is_set(synapse->flags, pcmk__synapse_failed)
387 && pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
388 graph->pending++;
389 }
390 }
391 crm_trace("Executing graph %d (%d synapses already completed, %d pending)",
392 graph->id, graph->completed, graph->pending);
393
394 // Execute any synapses that are ready
395 for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
396 pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
397
398 if ((graph->batch_limit > 0)
399 && (graph->pending >= graph->batch_limit)) {
400
401 crm_debug("Throttling graph execution: batch limit (%d) reached",
402 graph->batch_limit);
403 break;
404
405 } else if (pcmk_is_set(synapse->flags, pcmk__synapse_failed)) {
406 graph->skipped++;
407 continue;
408
409 } else if (pcmk_any_flags_set(synapse->flags,
410 pcmk__synapse_confirmed
411 |pcmk__synapse_executed)) {
412 continue; // Already handled
413
414 } else if (should_fire_synapse(graph, synapse)) {
415 graph->fired++;
416 if (fire_synapse(graph, synapse) != pcmk_rc_ok) {
417 crm_err("Synapse %d failed to fire", synapse->id);
418 log_level = LOG_ERR;
419 graph->abort_priority = INFINITY;
420 graph->incomplete++;
421 graph->fired--;
422 }
423
424 if (!(pcmk_is_set(synapse->flags, pcmk__synapse_confirmed))) {
425 graph->pending++;
426 }
427
428 } else {
429 crm_trace("Synapse %d cannot fire", synapse->id);
430 graph->incomplete++;
431 }
432 }
433
434 if ((graph->pending == 0) && (graph->fired == 0)) {
435 graph->complete = true;
436
437 if ((graph->incomplete != 0) && (graph->abort_priority <= 0)) {
438 log_level = LOG_WARNING;
439 pass_result = pcmk__graph_terminated;
440 status = "Terminated";
441
442 } else if (graph->skipped != 0) {
443 log_level = LOG_NOTICE;
444 pass_result = pcmk__graph_complete;
445 status = "Stopped";
446
447 } else {
448 log_level = LOG_NOTICE;
449 pass_result = pcmk__graph_complete;
450 status = "Complete";
451 }
452
453 } else if (graph->fired == 0) {
454 pass_result = pcmk__graph_pending;
455 }
456
457 do_crm_log(log_level,
458 "Transition %d (Complete=%d, Pending=%d,"
459 " Fired=%d, Skipped=%d, Incomplete=%d, Source=%s): %s",
460 graph->id, graph->completed, graph->pending, graph->fired,
461 graph->skipped, graph->incomplete, graph->source, status);
462
463 return pass_result;
464 }
465
466
467 /*
468 * Functions for unpacking transition graph XML into structs
469 */
470
471 /*!
472 * \internal
473 * \brief Unpack a transition graph action from XML
474 *
475 * \param[in] parent Synapse that action is part of
476 * \param[in] xml_action Action XML to unparse
477 *
478 * \return Newly allocated action on success, or NULL otherwise
479 */
480 static pcmk__graph_action_t *
481 unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action)
482 {
483 enum pcmk__graph_action_type action_type;
484 pcmk__graph_action_t *action = NULL;
485 const char *value = ID(xml_action);
486
487 if (value == NULL) {
488 crm_err("Ignoring transition graph action without id (bug?)");
489 crm_log_xml_trace(xml_action, "invalid");
490 return NULL;
491 }
492
493 if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_RSC_OP)) {
494 action_type = pcmk__rsc_graph_action;
495
496 } else if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_PSEUDO_EVENT)) {
497 action_type = pcmk__pseudo_graph_action;
498
499 } else if (pcmk__xe_is(xml_action, XML_GRAPH_TAG_CRM_EVENT)) {
500 action_type = pcmk__cluster_graph_action;
501
502 } else {
503 crm_err("Ignoring transition graph action of unknown type '%s' (bug?)",
504 xml_action->name);
505 crm_log_xml_trace(xml_action, "invalid");
506 return NULL;
507 }
508
509 action = calloc(1, sizeof(pcmk__graph_action_t));
510 if (action == NULL) {
511 crm_perror(LOG_CRIT, "Cannot unpack transition graph action");
512 crm_log_xml_trace(xml_action, "lost");
513 return NULL;
514 }
515
516 pcmk__scan_min_int(value, &(action->id), -1);
517 action->type = pcmk__rsc_graph_action;
518 action->xml = copy_xml(xml_action);
519 action->synapse = parent;
520 action->type = action_type;
521 action->params = xml2list(action->xml);
522
523 value = g_hash_table_lookup(action->params, "CRM_meta_timeout");
524 pcmk__scan_min_int(value, &(action->timeout), 0);
525
526 /* Take start-delay into account for the timeout of the action timer */
527 value = g_hash_table_lookup(action->params, "CRM_meta_start_delay");
528 {
529 int start_delay;
530
531 pcmk__scan_min_int(value, &start_delay, 0);
532 action->timeout += start_delay;
533 }
534
535 if (pcmk__guint_from_hash(action->params,
536 CRM_META "_" XML_LRM_ATTR_INTERVAL, 0,
537 &(action->interval_ms)) != pcmk_rc_ok) {
538 action->interval_ms = 0;
539 }
540
541 value = g_hash_table_lookup(action->params, "CRM_meta_can_fail");
542 if (value != NULL) {
543 int can_fail = 0;
544
545 if ((crm_str_to_boolean(value, &can_fail) > 0) && (can_fail > 0)) {
546 pcmk__set_graph_action_flags(action, pcmk__graph_action_can_fail);
547 } else {
548 pcmk__clear_graph_action_flags(action, pcmk__graph_action_can_fail);
549 }
550
551 #ifndef PCMK__COMPAT_2_0
552 if (pcmk_is_set(action->flags, pcmk__graph_action_can_fail)) {
553 crm_warn("Support for the can_fail meta-attribute is deprecated"
554 " and will be removed in a future release");
555 }
556 #endif
557 }
558
559 crm_trace("Action %d has timer set to %dms", action->id, action->timeout);
560
561 return action;
562 }
563
564 /*!
565 * \internal
566 * \brief Unpack transition graph synapse from XML
567 *
568 * \param[in,out] new_graph Transition graph that synapse is part of
569 * \param[in] xml_synapse Synapse XML
570 *
571 * \return Newly allocated synapse on success, or NULL otherwise
572 */
573 static pcmk__graph_synapse_t *
574 unpack_synapse(pcmk__graph_t *new_graph, const xmlNode *xml_synapse)
575 {
576 const char *value = NULL;
577 xmlNode *action_set = NULL;
578 pcmk__graph_synapse_t *new_synapse = NULL;
579
580 crm_trace("Unpacking synapse %s", ID(xml_synapse));
581
582 new_synapse = calloc(1, sizeof(pcmk__graph_synapse_t));
583 if (new_synapse == NULL) {
584 return NULL;
585 }
586
587 pcmk__scan_min_int(ID(xml_synapse), &(new_synapse->id), 0);
588
589 value = crm_element_value(xml_synapse, XML_CIB_ATTR_PRIORITY);
590 pcmk__scan_min_int(value, &(new_synapse->priority), 0);
591
592 CRM_CHECK(new_synapse->id >= 0, free(new_synapse);
593 return NULL);
594
595 new_graph->num_synapses++;
596
597 crm_trace("Unpacking synapse %s action sets",
598 crm_element_value(xml_synapse, XML_ATTR_ID));
599
600 for (action_set = first_named_child(xml_synapse, "action_set");
601 action_set != NULL; action_set = crm_next_same_xml(action_set)) {
602
603 for (xmlNode *action = pcmk__xml_first_child(action_set);
604 action != NULL; action = pcmk__xml_next(action)) {
605
606 pcmk__graph_action_t *new_action = unpack_action(new_synapse,
607 action);
608
609 if (new_action == NULL) {
610 continue;
611 }
612
613 crm_trace("Adding action %d to synapse %d",
614 new_action->id, new_synapse->id);
615 new_graph->num_actions++;
616 new_synapse->actions = g_list_append(new_synapse->actions,
617 new_action);
618 }
619 }
620
621 crm_trace("Unpacking synapse %s inputs", ID(xml_synapse));
622
623 for (xmlNode *inputs = first_named_child(xml_synapse, "inputs");
624 inputs != NULL; inputs = crm_next_same_xml(inputs)) {
625
626 for (xmlNode *trigger = first_named_child(inputs, "trigger");
627 trigger != NULL; trigger = crm_next_same_xml(trigger)) {
628
629 for (xmlNode *input = pcmk__xml_first_child(trigger);
630 input != NULL; input = pcmk__xml_next(input)) {
631
632 pcmk__graph_action_t *new_input = unpack_action(new_synapse,
633 input);
634
635 if (new_input == NULL) {
636 continue;
637 }
638
639 crm_trace("Adding input %d to synapse %d",
640 new_input->id, new_synapse->id);
641
642 new_synapse->inputs = g_list_append(new_synapse->inputs,
643 new_input);
644 }
645 }
646 }
647
648 return new_synapse;
649 }
650
651 /*!
652 * \internal
653 * \brief Unpack transition graph XML
654 *
655 * \param[in] xml_graph Transition graph XML to unpack
656 * \param[in] reference Where the XML came from (for logging)
657 *
658 * \return Newly allocated transition graph on success, NULL otherwise
659 * \note The caller is responsible for freeing the return value using
660 * pcmk__free_graph().
661 * \note The XML is expected to be structured like:
662 <transition_graph ...>
663 <synapse id="0">
664 <action_set>
665 <rsc_op id="2" ...>
666 ...
667 </action_set>
668 <inputs>
669 <rsc_op id="1" ...
670 ...
671 </inputs>
672 </synapse>
673 ...
674 </transition_graph>
675 */
676 pcmk__graph_t *
677 pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
678 {
679 pcmk__graph_t *new_graph = NULL;
680
681 new_graph = calloc(1, sizeof(pcmk__graph_t));
682 if (new_graph == NULL) {
683 return NULL;
684 }
685
686 new_graph->source = strdup((reference == NULL)? "unknown" : reference);
687 if (new_graph->source == NULL) {
688 free(new_graph);
689 return NULL;
690 }
691
692 new_graph->id = -1;
693 new_graph->abort_priority = 0;
694 new_graph->network_delay = 0;
695 new_graph->stonith_timeout = 0;
696 new_graph->completion_action = pcmk__graph_done;
697
698 // Parse top-level attributes from <transition_graph>
699 if (xml_graph != NULL) {
700 const char *buf = crm_element_value(xml_graph, "transition_id");
701
702 CRM_CHECK(buf != NULL, free(new_graph);
703 return NULL);
704 pcmk__scan_min_int(buf, &(new_graph->id), -1);
705
706 buf = crm_element_value(xml_graph, "cluster-delay");
707 CRM_CHECK(buf != NULL, free(new_graph);
708 return NULL);
709 new_graph->network_delay = crm_parse_interval_spec(buf);
710
711 buf = crm_element_value(xml_graph, "stonith-timeout");
712 if (buf == NULL) {
713 new_graph->stonith_timeout = new_graph->network_delay;
714 } else {
715 new_graph->stonith_timeout = crm_parse_interval_spec(buf);
716 }
717
718 // Use 0 (dynamic limit) as default/invalid, -1 (no limit) as minimum
719 buf = crm_element_value(xml_graph, "batch-limit");
720 if ((buf == NULL)
721 || (pcmk__scan_min_int(buf, &(new_graph->batch_limit),
722 -1) != pcmk_rc_ok)) {
723 new_graph->batch_limit = 0;
724 }
725
726 buf = crm_element_value(xml_graph, "migration-limit");
727 pcmk__scan_min_int(buf, &(new_graph->migration_limit), -1);
728
729 pcmk__str_update(&(new_graph->failed_stop_offset),
730 crm_element_value(xml_graph, "failed-stop-offset"));
731 pcmk__str_update(&(new_graph->failed_start_offset),
732 crm_element_value(xml_graph, "failed-start-offset"));
733
734 if (crm_element_value_epoch(xml_graph, "recheck-by",
735 &(new_graph->recheck_by)) != pcmk_ok) {
736 new_graph->recheck_by = 0;
737 }
738 }
739
740 // Unpack each child <synapse> element
741 for (const xmlNode *synapse_xml = first_named_child(xml_graph, "synapse");
742 synapse_xml != NULL; synapse_xml = crm_next_same_xml(synapse_xml)) {
743
744 pcmk__graph_synapse_t *new_synapse = unpack_synapse(new_graph,
745 synapse_xml);
746
747 if (new_synapse != NULL) {
748 new_graph->synapses = g_list_append(new_graph->synapses,
749 new_synapse);
750 }
751 }
752
753 crm_debug("Unpacked transition %d from %s: %d actions in %d synapses",
754 new_graph->id, new_graph->source, new_graph->num_actions,
755 new_graph->num_synapses);
756
757 return new_graph;
758 }
759
760
761 /*
762 * Functions for freeing transition graph objects
763 */
764
765 /*!
766 * \internal
767 * \brief Free a transition graph action object
768 *
769 * \param[in,out] user_data Action to free
770 */
771 static void
772 free_graph_action(gpointer user_data)
773 {
774 pcmk__graph_action_t *action = user_data;
775
776 if (action->timer != 0) {
777 crm_warn("Cancelling timer for graph action %d", action->id);
778 g_source_remove(action->timer);
779 }
780 if (action->params != NULL) {
781 g_hash_table_destroy(action->params);
782 }
783 free_xml(action->xml);
784 free(action);
785 }
786
787 /*!
788 * \internal
789 * \brief Free a transition graph synapse object
790 *
791 * \param[in,out] user_data Synapse to free
792 */
793 static void
794 free_graph_synapse(gpointer user_data)
795 {
796 pcmk__graph_synapse_t *synapse = user_data;
797
798 g_list_free_full(synapse->actions, free_graph_action);
799 g_list_free_full(synapse->inputs, free_graph_action);
800 free(synapse);
801 }
802
803 /*!
804 * \internal
805 * \brief Free a transition graph object
806 *
807 * \param[in,out] graph Transition graph to free
808 */
809 void
810 pcmk__free_graph(pcmk__graph_t *graph)
811 {
812 if (graph != NULL) {
813 g_list_free_full(graph->synapses, free_graph_synapse);
814 free(graph->source);
815 free(graph->failed_stop_offset);
816 free(graph->failed_start_offset);
817 free(graph);
818 }
819 }
820
821
822 /*
823 * Other transition graph utilities
824 */
825
826 /*!
827 * \internal
828 * \brief Synthesize an executor event from a graph action
829 *
830 * \param[in] resource If not NULL, use greater call ID than in this XML
831 * \param[in] action Graph action
832 * \param[in] status What to use as event execution status
833 * \param[in] rc What to use as event exit status
834 * \param[in] exit_reason What to use as event exit reason
835 *
836 * \return Newly allocated executor event on success, or NULL otherwise
837 */
838 lrmd_event_data_t *
839 pcmk__event_from_graph_action(const xmlNode *resource,
840 const pcmk__graph_action_t *action,
841 int status, int rc, const char *exit_reason)
842 {
843 lrmd_event_data_t *op = NULL;
844 GHashTableIter iter;
845 const char *name = NULL;
846 const char *value = NULL;
847 xmlNode *action_resource = NULL;
848
849 CRM_CHECK(action != NULL, return NULL);
850 CRM_CHECK(action->type == pcmk__rsc_graph_action, return NULL);
851
852 action_resource = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
853 CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "invalid");
854 return NULL);
855
856 op = lrmd_new_event(ID(action_resource),
857 crm_element_value(action->xml, XML_LRM_ATTR_TASK),
858 action->interval_ms);
859 lrmd__set_result(op, rc, status, exit_reason);
|
CID (unavailable; MK=093568d06296b4547cd214e2c44e00c1) (#1 of 1): Use of 32-bit time_t (Y2K38_SAFETY): |
|
(1) Event store_truncates_time_t: |
A "time_t" value is stored in an integer with too few bits to accommodate it. The expression "time(NULL)" is cast to "unsigned int". |
860 op->t_run = time(NULL);
861 op->t_rcchange = op->t_run;
862 op->params = pcmk__strkey_table(free, free);
863
864 g_hash_table_iter_init(&iter, action->params);
865 while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
866 g_hash_table_insert(op->params, strdup(name), strdup(value));
867 }
868
869 for (xmlNode *xop = pcmk__xml_first_child(resource); xop != NULL;
870 xop = pcmk__xml_next(xop)) {
871 int tmp = 0;
872
873 crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp);
874 crm_debug("Got call_id=%d for %s", tmp, ID(resource));
875 if (tmp > op->call_id) {
876 op->call_id = tmp;
877 }
878 }
879
880 op->call_id++;
881 return op;
882 }
883