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