pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_sched_actions.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 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> // bool, true, false
13#include <stdio.h>
14#include <sys/param.h>
15
16#include <glib.h>
17#include <libxml/tree.h> // xmlNode
18
19#include <crm/lrmd_internal.h>
21#include <pacemaker-internal.h>
23
34static uint32_t
35action_flags_for_ordering(pcmk_action_t *action, const pcmk_node_t *node)
36{
37 bool runnable = false;
38 uint32_t flags;
39
40 // For non-resource actions, return the action flags
41 if (action->rsc == NULL) {
42 return action->flags;
43 }
44
45 /* For non-clone resources, or a clone action not assigned to a node,
46 * return the flags as determined by the resource method without a node
47 * specified.
48 */
49 flags = action->rsc->priv->cmds->action_flags(action, NULL);
50 if ((node == NULL) || !pcmk__is_clone(action->rsc)) {
51 return flags;
52 }
53
54 /* Otherwise (i.e., for clone resource actions on a specific node), first
55 * remember whether the non-node-specific action is runnable.
56 */
58
59 // Then recheck the resource method with the node
60 flags = action->rsc->priv->cmds->action_flags(action, node);
61
62 /* For clones in ordering constraints, the node-specific "runnable" doesn't
63 * matter, just the non-node-specific setting (i.e., is the action runnable
64 * anywhere).
65 *
66 * This applies only to runnable, and only for ordering constraints. This
67 * function shouldn't be used for other types of constraints without
68 * changes. Not very satisfying, but it's logical and appears to work well.
69 */
70 if (runnable && !pcmk_is_set(flags, pcmk__action_runnable)) {
73 }
74 return flags;
75}
76
95static char *
96action_uuid_for_ordering(const char *first_uuid,
97 const pcmk_resource_t *first_rsc)
98{
99 guint interval_ms = 0;
100 char *uuid = NULL;
101 char *rid = NULL;
102 char *first_task_str = NULL;
104 enum pcmk__action_type remapped_task = pcmk__action_unspecified;
105
106 // Only non-notify actions for collective resources need remapping
107 if ((strstr(first_uuid, PCMK_ACTION_NOTIFY) != NULL)
108 || (first_rsc->priv->variant < pcmk__rsc_variant_group)) {
109 goto done;
110 }
111
112 // Only non-recurring actions need remapping
113 pcmk__assert(parse_op_key(first_uuid, &rid, &first_task_str, &interval_ms));
114 if (interval_ms > 0) {
115 goto done;
116 }
117
118 first_task = pcmk__parse_action(first_task_str);
119 switch (first_task) {
125 remapped_task = first_task + 1;
126 break;
132 remapped_task = first_task;
133 break;
137 break;
138 default:
139 crm_err("Unknown action '%s' in ordering", first_task_str);
140 break;
141 }
142
143 if (remapped_task != pcmk__action_unspecified) {
144 /* If a clone or bundle has notifications enabled, the ordering will be
145 * relative to when notifications have been sent for the remapped task.
146 */
147 if (pcmk_is_set(first_rsc->flags, pcmk__rsc_notify)
148 && (pcmk__is_clone(first_rsc) || pcmk__is_bundled(first_rsc))) {
149 uuid = pcmk__notify_key(rid, "confirmed-post",
150 pcmk__action_text(remapped_task));
151 } else {
152 uuid = pcmk__op_key(rid, pcmk__action_text(remapped_task), 0);
153 }
154 pcmk__rsc_trace(first_rsc,
155 "Remapped action UUID %s to %s for ordering purposes",
156 first_uuid, uuid);
157 }
158
159done:
160 free(first_task_str);
161 free(rid);
162 return (uuid != NULL)? uuid : pcmk__str_copy(first_uuid);
163}
164
181static pcmk_action_t *
182action_for_ordering(pcmk_action_t *action)
183{
185 pcmk_resource_t *rsc = action->rsc;
186
187 if (rsc == NULL) {
188 return result;
189 }
190
192 && (action->uuid != NULL)) {
193 char *uuid = action_uuid_for_ordering(action->uuid, rsc);
194
195 result = find_first_action(rsc->priv->actions, uuid, NULL, NULL);
196 if (result == NULL) {
197 crm_warn("Not remapping %s to %s because %s does not have "
198 "remapped action", action->uuid, uuid, rsc->id);
199 result = action;
200 }
201 free(uuid);
202 }
203 return result;
204}
205
225static inline uint32_t
226update(pcmk_resource_t *rsc, pcmk_action_t *first, pcmk_action_t *then,
227 const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type,
229{
230 return rsc->priv->cmds->update_ordered_actions(first, then, node, flags,
231 filter, type, scheduler);
232}
233
247static uint32_t
248update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then,
249 uint32_t first_flags, uint32_t then_flags,
252{
253 uint32_t changed = pcmk__updated_none;
254
255 /* The node will only be used for clones. If interleaved, node will be NULL,
256 * otherwise the ordering scope will be limited to the node. Normally, the
257 * whole 'then' clone should restart if 'first' is restarted, so then->node
258 * is needed.
259 */
260 pcmk_node_t *node = then->node;
261
263 /* For unfencing, only instances of 'then' on the same node as 'first'
264 * (the unfencing operation) should restart, so reset node to
265 * first->node, at which point this case is handled like a normal
266 * pcmk__ar_first_implies_then.
267 */
271 node = first->node;
272 pcmk__rsc_trace(then->rsc,
273 "%s then %s: mapped "
274 "pcmk__ar_first_implies_same_node_then to "
275 "pcmk__ar_first_implies_then on %s",
276 first->uuid, then->uuid, pcmk__node_name(node));
277 }
278
280 if (then->rsc != NULL) {
281 changed |= update(then->rsc, first, then, node,
282 first_flags & pcmk__action_optional,
285 } else if (!pcmk_is_set(first_flags, pcmk__action_optional)
289 }
290 pcmk__rsc_trace(then->rsc,
291 "%s then %s: %s after pcmk__ar_first_implies_then",
292 first->uuid, then->uuid,
293 (changed? "changed" : "unchanged"));
294 }
295
297 && (then->rsc != NULL)) {
300
301 changed |= update(then->rsc, first, then, node, first_flags, restart,
303 pcmk__rsc_trace(then->rsc,
304 "%s then %s: %s after pcmk__ar_intermediate_stop",
305 first->uuid, then->uuid,
306 (changed? "changed" : "unchanged"));
307 }
308
310 if (first->rsc != NULL) {
311 changed |= update(first->rsc, first, then, node, first_flags,
314 } else if (!pcmk_is_set(first_flags, pcmk__action_optional)
318 }
319 pcmk__rsc_trace(then->rsc,
320 "%s then %s: %s after pcmk__ar_then_implies_first",
321 first->uuid, then->uuid,
322 (changed? "changed" : "unchanged"));
323 }
324
326 if (then->rsc != NULL) {
327 changed |= update(then->rsc, first, then, node,
328 first_flags & pcmk__action_optional,
331 }
332 pcmk__rsc_trace(then->rsc,
333 "%s then %s: %s after "
334 "pcmk__ar_promoted_then_implies_first",
335 first->uuid, then->uuid,
336 (changed? "changed" : "unchanged"));
337 }
338
340 if (then->rsc != NULL) {
341 changed |= update(then->rsc, first, then, node, first_flags,
343 scheduler);
344
345 } else if (pcmk_is_set(first_flags, pcmk__action_runnable)) {
346 // We have another runnable instance of "first"
347 then->runnable_before++;
348
349 /* Mark "then" as runnable if it requires a certain number of
350 * "before" instances to be runnable, and they now are.
351 */
352 if ((then->runnable_before >= then->required_runnable_before)
354
357 }
358 }
359 pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_min_runnable",
360 first->uuid, then->uuid,
361 (changed? "changed" : "unchanged"));
362 }
363
365 && (then->rsc != NULL)) {
366
367 if (!pcmk_is_set(first_flags, pcmk__action_runnable)
368 && (first->rsc != NULL)
369 && (first->rsc->priv->active_nodes != NULL)) {
370
371 pcmk__rsc_trace(then->rsc,
372 "%s then %s: ignoring because first is stopping",
373 first->uuid, then->uuid);
374 order->flags = pcmk__ar_none;
375 } else {
376 changed |= update(then->rsc, first, then, node, first_flags,
379 }
380 pcmk__rsc_trace(then->rsc,
381 "%s then %s: %s after pcmk__ar_nested_remote_probe",
382 first->uuid, then->uuid,
383 (changed? "changed" : "unchanged"));
384 }
385
387 if (then->rsc != NULL) {
388 changed |= update(then->rsc, first, then, node, first_flags,
391
392 } else if (!pcmk_is_set(first_flags, pcmk__action_runnable)
394
397 }
398 pcmk__rsc_trace(then->rsc,
399 "%s then %s: %s after pcmk__ar_unrunnable_first_blocks",
400 first->uuid, then->uuid,
401 (changed? "changed" : "unchanged"));
402 }
403
405 if (then->rsc != NULL) {
406 changed |= update(then->rsc, first, then, node, first_flags,
409 }
410 pcmk__rsc_trace(then->rsc,
411 "%s then %s: %s after "
412 "pcmk__ar_unmigratable_then_blocks",
413 first->uuid, then->uuid,
414 (changed? "changed" : "unchanged"));
415 }
416
418 if (then->rsc != NULL) {
419 changed |= update(then->rsc, first, then, node, first_flags,
421 scheduler);
422 }
423 pcmk__rsc_trace(then->rsc,
424 "%s then %s: %s after pcmk__ar_first_else_then",
425 first->uuid, then->uuid,
426 (changed? "changed" : "unchanged"));
427 }
428
429 if (pcmk_is_set(order->flags, pcmk__ar_ordered)) {
430 if (then->rsc != NULL) {
431 changed |= update(then->rsc, first, then, node, first_flags,
433 scheduler);
434 }
435 pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_ordered",
436 first->uuid, then->uuid,
437 (changed? "changed" : "unchanged"));
438 }
439
441 if (then->rsc != NULL) {
442 changed |= update(then->rsc, first, then, node, first_flags,
444 scheduler);
445 }
446 pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_asymmetric",
447 first->uuid, then->uuid,
448 (changed? "changed" : "unchanged"));
449 }
450
453 && !pcmk_is_set(first_flags, pcmk__action_optional)) {
454
455 pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
456 then->uuid, first->uuid);
458 // Don't bother marking 'then' as changed just for this
459 }
460
462 && !pcmk_is_set(then_flags, pcmk__action_optional)) {
463
464 pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
465 first->uuid, then->uuid);
467 // Don't bother marking 'first' as changed just for this
468 }
469
470 if (pcmk_any_flags_set(order->flags, pcmk__ar_first_implies_then
473 && (first->rsc != NULL)
477 && pcmk__str_eq(first->task, PCMK_ACTION_STOP, pcmk__str_none)) {
478
479 /* @TODO This seems odd; why wouldn't an unrunnable "first" already
480 * block "then" before this? Note that the unmanaged-stop-{1,2}
481 * scheduler regression tests and the test CIB for T209 have tests for
482 * "stop then stop" relations that would be good for checking any
483 * changes.
484 */
488 }
489 pcmk__rsc_trace(then->rsc,
490 "%s then %s: %s after checking whether first "
491 "is blocked, unmanaged, unrunnable stop",
492 first->uuid, then->uuid,
493 (changed? "changed" : "unchanged"));
494 }
495
496 return changed;
497}
498
499// Convenience macros for logging action properties
500
501#define action_type_str(flags) \
502 (pcmk_is_set((flags), pcmk__action_pseudo)? "pseudo-action" : "action")
503
504#define action_optional_str(flags) \
505 (pcmk_is_set((flags), pcmk__action_optional)? "optional" : "required")
506
507#define action_runnable_str(flags) \
508 (pcmk_is_set((flags), pcmk__action_runnable)? "runnable" : "unrunnable")
509
510#define action_node_str(a) \
511 (((a)->node == NULL)? "no node" : (a)->node->priv->name)
512
520void
523{
524 GList *lpc = NULL;
525 uint32_t changed = pcmk__updated_none;
526 int last_flags = then->flags;
527
528 pcmk__rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
529 action_type_str(then->flags), then->uuid,
532
533 if (then->required_runnable_before > 0) {
534 /* Initialize current known "runnable before" actions. As
535 * update_action_for_ordering_flags() is called for each of then's
536 * before actions, this number will increment as runnable 'first'
537 * actions are encountered.
538 */
539 then->runnable_before = 0;
540
541 /* The pcmk__ar_min_runnable clause of
542 * update_action_for_ordering_flags() (called below)
543 * will reset runnable if appropriate.
544 */
546 }
547
548 for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
549 pcmk__related_action_t *other = lpc->data;
550 pcmk_action_t *first = other->action;
551
552 pcmk_node_t *then_node = then->node;
553 pcmk_node_t *first_node = first->node;
554
555 const uint32_t target = pcmk__rsc_node_assigned;
556
557 if ((first->rsc != NULL)
558 && pcmk__is_group(first->rsc)
559 && pcmk__str_eq(first->task, PCMK_ACTION_START, pcmk__str_none)) {
560
561 first_node = first->rsc->priv->fns->location(first->rsc, NULL,
562 target);
563 if (first_node != NULL) {
564 pcmk__rsc_trace(first->rsc, "Found %s for 'first' %s",
565 pcmk__node_name(first_node), first->uuid);
566 }
567 }
568
569 if (pcmk__is_group(then->rsc)
570 && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)) {
571
572 then_node = then->rsc->priv->fns->location(then->rsc, NULL, target);
573 if (then_node != NULL) {
574 pcmk__rsc_trace(then->rsc, "Found %s for 'then' %s",
575 pcmk__node_name(then_node), then->uuid);
576 }
577 }
578
579 // Disable constraint if it only applies when on same node, but isn't
581 && (first_node != NULL) && (then_node != NULL)
582 && !pcmk__same_node(first_node, then_node)) {
583
584 pcmk__rsc_trace(then->rsc,
585 "Disabled ordering %s on %s then %s on %s: "
586 "not same node",
587 other->action->uuid, pcmk__node_name(first_node),
588 then->uuid, pcmk__node_name(then_node));
589 other->flags = pcmk__ar_none;
590 continue;
591 }
592
594
595 if ((first->rsc != NULL)
598
599 /* 'then' is required, so we must abandon 'first'
600 * (e.g. a required stop cancels any agent reload).
601 */
603 if (!strcmp(first->task, PCMK_ACTION_RELOAD_AGENT)) {
605 }
606 }
607
608 if ((first->rsc != NULL) && (then->rsc != NULL)
609 && (first->rsc != then->rsc) && !is_parent(then->rsc, first->rsc)) {
610 first = action_for_ordering(first);
611 }
612 if (first != other->action) {
613 pcmk__rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
614 then->uuid, first->uuid, other->action->uuid);
615 }
616
617 pcmk__rsc_trace(then->rsc,
618 "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s",
619 first->uuid, first->flags, then->uuid, then->flags,
620 other->flags, action_node_str(first));
621
622 if (first == other->action) {
623 /* 'first' was not remapped (e.g. from 'start' to 'running'), which
624 * could mean it is a non-resource action, a primitive resource
625 * action, or already expanded.
626 */
627 uint32_t first_flags, then_flags;
628
629 first_flags = action_flags_for_ordering(first, then_node);
630 then_flags = action_flags_for_ordering(then, first_node);
631
632 changed |= update_action_for_ordering_flags(first, then,
633 first_flags, then_flags,
634 other, scheduler);
635
636 /* 'first' was for a complex resource (clone, group, etc),
637 * create a new dependency if necessary
638 */
639 } else if (order_actions(first, then, other->flags)) {
640 /* This was the first time 'first' and 'then' were associated,
641 * start again to get the new actions_before list
642 */
644 pcmk__rsc_trace(then->rsc,
645 "Disabled ordering %s then %s in favor of %s "
646 "then %s",
647 other->action->uuid, then->uuid, first->uuid,
648 then->uuid);
649 other->flags = pcmk__ar_none;
650 }
651
652
653 if (pcmk_is_set(changed, pcmk__updated_first)) {
654 crm_trace("Re-processing %s and its 'after' actions "
655 "because it changed", first->uuid);
656 for (GList *lpc2 = first->actions_after; lpc2 != NULL;
657 lpc2 = lpc2->next) {
658 pcmk__related_action_t *other = lpc2->data;
659
661 }
663 }
664 }
665
666 if (then->required_runnable_before > 0) {
667 if (last_flags == then->flags) {
669 } else {
671 }
672 }
673
674 if (pcmk_is_set(changed, pcmk__updated_then)) {
675 crm_trace("Re-processing %s and its 'after' actions because it changed",
676 then->uuid);
677 if (pcmk_is_set(last_flags, pcmk__action_runnable)
680 }
682 for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
683 pcmk__related_action_t *other = lpc->data;
684
686 }
687 }
688}
689
690static inline bool
691is_primitive_action(const pcmk_action_t *action)
692{
693 return (action != NULL) && pcmk__is_primitive(action->rsc);
694}
695
704#define clear_action_flag_because(action, flag, reason) do { \
705 if (pcmk_is_set((action)->flags, (flag))) { \
706 pcmk__clear_action_flags(action, flag); \
707 if ((action)->rsc != (reason)->rsc) { \
708 char *reason_text = pe__action2reason((reason), (flag)); \
709 pe_action_set_reason((action), reason_text, false); \
710 free(reason_text); \
711 } \
712 } \
713 } while (0)
714
725static void
726handle_asymmetric_ordering(const pcmk_action_t *first, pcmk_action_t *then)
727{
728 /* Only resource actions after an unrunnable 'first' action need updates for
729 * asymmetric ordering.
730 */
731 if ((then->rsc == NULL)
733 return;
734 }
735
736 // Certain optional 'then' actions are unaffected by unrunnable 'first'
738 enum rsc_role_e then_rsc_role;
739
740 then_rsc_role = then->rsc->priv->fns->state(then->rsc, true);
741
742 if ((then_rsc_role == pcmk_role_stopped)
743 && pcmk__str_eq(then->task, PCMK_ACTION_STOP, pcmk__str_none)) {
744 /* If 'then' should stop after 'first' but is already stopped, the
745 * ordering is irrelevant.
746 */
747 return;
748 } else if ((then_rsc_role >= pcmk_role_started)
749 && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)
750 && pe__rsc_running_on_only(then->rsc, then->node)) {
751 /* Similarly if 'then' should start after 'first' but is already
752 * started on a single node.
753 */
754 return;
755 }
756 }
757
758 // 'First' can't run, so 'then' can't either
761}
762
774static void
775handle_restart_ordering(pcmk_action_t *first, pcmk_action_t *then,
776 uint32_t filter)
777{
778 const char *reason = NULL;
779
780 pcmk__assert(is_primitive_action(first) && is_primitive_action(then));
781
782 // We need to update the action in two cases:
783
784 // ... if 'then' is required
787 reason = "restart";
788 }
789
790 /* ... if 'then' is unrunnable action on same resource (if a resource
791 * should restart but can't start, we still want to stop)
792 */
796 && (first->rsc == then->rsc)) {
797 reason = "stop";
798 }
799
800 if (reason == NULL) {
801 return;
802 }
803
804 pcmk__rsc_trace(first->rsc, "Handling %s -> %s for %s",
805 first->uuid, then->uuid, reason);
806
807 // Make 'first' required if it is runnable
810 }
811
812 // Make 'first' required if 'then' is required
815 }
816
817 // Make 'first' unmigratable if 'then' is unmigratable
820 }
821
822 // Make 'then' unrunnable if 'first' is required but unrunnable
826 }
827}
828
851uint32_t
853 const pcmk_node_t *node, uint32_t flags,
854 uint32_t filter, uint32_t type,
856{
857 uint32_t changed = pcmk__updated_none;
858 uint32_t then_flags = 0U;
859 uint32_t first_flags = 0U;
860
861 pcmk__assert((first != NULL) && (then != NULL) && (scheduler != NULL));
862
863 then_flags = then->flags;
864 first_flags = first->flags;
866 handle_asymmetric_ordering(first, then);
867 }
868
870 && !pcmk_is_set(then_flags, pcmk__action_optional)) {
871 // Then is required, and implies first should be, too
872
875 && pcmk_is_set(first_flags, pcmk__action_optional)) {
877 }
878
882 }
883 }
884
886 && (then->rsc != NULL)
887 && (then->rsc->priv->orig_role == pcmk_role_promoted)
890
892
896 }
897 }
898
901
902 if (!pcmk_all_flags_set(then->flags, pcmk__action_migratable
905 }
906
909 }
910 }
911
915
918 }
919
924
927 }
928
934
936 }
937
939 handle_restart_ordering(first, then, filter);
940 }
941
942 if (then_flags != then->flags) {
944 pcmk__rsc_trace(then->rsc,
945 "%s on %s: flags are now %#.6x (was %#.6x) "
946 "because of 'first' %s (%#.6x)",
947 then->uuid, pcmk__node_name(then->node),
948 then->flags, then_flags, first->uuid, first->flags);
949
950 if ((then->rsc != NULL) && (then->rsc->priv->parent != NULL)) {
951 // Required to handle "X_stop then X_start" for cloned groups
953 }
954 }
955
956 if (first_flags != first->flags) {
958 pcmk__rsc_trace(first->rsc,
959 "%s on %s: flags are now %#.6x (was %#.6x) "
960 "because of 'then' %s (%#.6x)",
961 first->uuid, pcmk__node_name(first->node),
962 first->flags, first_flags, then->uuid, then->flags);
963 }
964
965 return changed;
966}
967
976void
977pcmk__log_action(const char *pre_text, const pcmk_action_t *action,
978 bool details)
979{
980 const char *node_uname = NULL;
981 const char *node_uuid = NULL;
982 const char *desc = NULL;
983
984 CRM_CHECK(action != NULL, return);
985
986 if (!pcmk_is_set(action->flags, pcmk__action_pseudo)) {
987 if (action->node != NULL) {
988 node_uname = action->node->priv->name;
989 node_uuid = action->node->priv->id;
990 } else {
991 node_uname = "<none>";
992 }
993 }
994
995 switch (pcmk__parse_action(action->task)) {
999 desc = "Pseudo ";
1000 } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
1001 desc = "Optional ";
1002 } else if (!pcmk_is_set(action->flags, pcmk__action_runnable)) {
1003 desc = "!!Non-Startable!! ";
1004 } else {
1005 desc = "(Provisional) ";
1006 }
1007 crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
1008 ((pre_text == NULL)? "" : pre_text),
1009 ((pre_text == NULL)? "" : ": "),
1010 desc, action->id, action->uuid,
1011 (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1012 (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1013 (node_uuid? ")" : ""));
1014 break;
1015 default:
1017 desc = "Optional ";
1018 } else if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
1019 desc = "Pseudo ";
1020 } else if (!pcmk_is_set(action->flags, pcmk__action_runnable)) {
1021 desc = "!!Non-Startable!! ";
1022 } else {
1023 desc = "(Provisional) ";
1024 }
1025 crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
1026 ((pre_text == NULL)? "" : pre_text),
1027 ((pre_text == NULL)? "" : ": "),
1028 desc, action->id, action->uuid,
1029 (action->rsc? action->rsc->id : "<none>"),
1030 (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1031 (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1032 (node_uuid? ")" : ""));
1033 break;
1034 }
1035
1036 if (details) {
1037 const GList *iter = NULL;
1038 const pcmk__related_action_t *other = NULL;
1039
1040 crm_trace("\t\t====== Preceding Actions");
1041 for (iter = action->actions_before; iter != NULL; iter = iter->next) {
1042 other = (const pcmk__related_action_t *) iter->data;
1043 pcmk__log_action("\t\t", other->action, false);
1044 }
1045 crm_trace("\t\t====== Subsequent Actions");
1046 for (iter = action->actions_after; iter != NULL; iter = iter->next) {
1047 other = (const pcmk__related_action_t *) iter->data;
1048 pcmk__log_action("\t\t", other->action, false);
1049 }
1050 crm_trace("\t\t====== End");
1051
1052 } else {
1053 crm_trace("\t\t(before=%d, after=%d)",
1054 g_list_length(action->actions_before),
1055 g_list_length(action->actions_after));
1056 }
1057}
1058
1069{
1070 char *shutdown_id = NULL;
1071 pcmk_action_t *shutdown_op = NULL;
1072
1073 pcmk__assert(node != NULL);
1074
1075 shutdown_id = crm_strdup_printf("%s-%s", PCMK_ACTION_DO_SHUTDOWN,
1076 node->priv->name);
1077
1078 shutdown_op = custom_action(NULL, shutdown_id, PCMK_ACTION_DO_SHUTDOWN,
1079 node, FALSE, node->priv->scheduler);
1080
1081 pcmk__order_stops_before_shutdown(node, shutdown_op);
1083 return shutdown_op;
1084}
1085
1097static void
1098add_op_digest_to_xml(const lrmd_event_data_t *op, xmlNode *update)
1099{
1100 char *digest = NULL;
1101 xmlNode *args_xml = NULL;
1102
1103 if (op->params == NULL) {
1104 return;
1105 }
1106 args_xml = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
1107 g_hash_table_foreach(op->params, hash2field, args_xml);
1109 digest = pcmk__digest_operation(args_xml);
1110 crm_xml_add(update, PCMK__XA_OP_DIGEST, digest);
1111 pcmk__xml_free(args_xml);
1112 free(digest);
1113}
1114
1115#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1116
1130xmlNode *
1132 const char *caller_version, int target_rc,
1133 const char *node, const char *origin)
1134{
1135 char *key = NULL;
1136 char *magic = NULL;
1137 char *op_id = NULL;
1138 char *op_id_additional = NULL;
1139 char *local_user_data = NULL;
1140 const char *exit_reason = NULL;
1141
1142 xmlNode *xml_op = NULL;
1143 const char *task = NULL;
1144
1145 CRM_CHECK(op != NULL, return NULL);
1146 crm_trace("Creating history XML for %s-interval %s action for %s on %s "
1147 "(DC version: %s, origin: %s)",
1149 ((node == NULL)? "no node" : node), caller_version, origin);
1150
1151 task = op->op_type;
1152
1153 /* Record a successful agent reload as a start, and a failed one as a
1154 * monitor, to make life easier for the scheduler when determining the
1155 * current state.
1156 *
1157 * @COMPAT We should check "reload" here only if the operation was for a
1158 * pre-OCF-1.1 resource agent, but we don't know that here, and we should
1159 * only ever get results for actions scheduled by us, so we can reasonably
1160 * assume any "reload" is actually a pre-1.1 agent reload.
1161 *
1162 * @TODO This remapping can make log messages with task confusing for users
1163 * (for example, an "Initiating reload ..." followed by "... start ...
1164 * confirmed"). Either do this remapping in the scheduler if possible, or
1165 * store the original task in a new XML attribute for later logging.
1166 */
1168 NULL)) {
1169 if (op->op_status == PCMK_EXEC_DONE) {
1170 task = PCMK_ACTION_START;
1171 } else {
1172 task = PCMK_ACTION_MONITOR;
1173 }
1174 }
1175
1176 key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
1177 if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
1178 const char *n_type = crm_meta_value(op->params, "notify_type");
1179 const char *n_task = crm_meta_value(op->params, "notify_operation");
1180
1181 CRM_LOG_ASSERT(n_type != NULL);
1182 CRM_LOG_ASSERT(n_task != NULL);
1183 op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
1184
1185 if (op->op_status != PCMK_EXEC_PENDING) {
1186 /* Ignore notify errors.
1187 *
1188 * @TODO It might be better to keep the correct result here, and
1189 * ignore it in process_graph_event().
1190 */
1192 }
1193
1194 /* Migration history is preserved separately, which usually matters for
1195 * multiple nodes and is important for future cluster transitions.
1196 */
1198 PCMK_ACTION_MIGRATE_FROM, NULL)) {
1199 op_id = strdup(key);
1200
1201 } else if (did_rsc_op_fail(op, target_rc)) {
1202 op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
1203 if (op->interval_ms == 0) {
1204 /* Ensure 'last' gets updated, in case PCMK_META_RECORD_PENDING is
1205 * true
1206 */
1207 op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
1208 }
1209 exit_reason = op->exit_reason;
1210
1211 } else if (op->interval_ms > 0) {
1212 op_id = strdup(key);
1213
1214 } else {
1215 op_id = pcmk__op_key(op->rsc_id, "last", 0);
1216 }
1217
1218 again:
1220 op_id);
1221 if (xml_op == NULL) {
1223 }
1224
1225 if (op->user_data == NULL) {
1226 crm_debug("Generating fake transition key for: " PCMK__OP_FMT
1227 " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
1228 op->call_id, origin);
1229 local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
1230 FAKE_TE_ID);
1231 op->user_data = local_user_data;
1232 }
1233
1234 if (magic == NULL) {
1235 magic = crm_strdup_printf("%d:%d;%s", op->op_status, op->rc,
1236 (const char *) op->user_data);
1237 }
1238
1239 crm_xml_add(xml_op, PCMK_XA_ID, op_id);
1240 crm_xml_add(xml_op, PCMK__XA_OPERATION_KEY, key);
1241 crm_xml_add(xml_op, PCMK_XA_OPERATION, task);
1242 crm_xml_add(xml_op, PCMK_XA_CRM_DEBUG_ORIGIN, origin);
1243 crm_xml_add(xml_op, PCMK_XA_CRM_FEATURE_SET, caller_version);
1245 crm_xml_add(xml_op, PCMK__XA_TRANSITION_MAGIC, magic);
1246 crm_xml_add(xml_op, PCMK_XA_EXIT_REASON, pcmk__s(exit_reason, ""));
1247 crm_xml_add(xml_op, PCMK__META_ON_NODE, node); // For context during triage
1248
1250 crm_xml_add_int(xml_op, PCMK__XA_RC_CODE, op->rc);
1253
1254 if ((op->t_run > 0) || (op->t_rcchange > 0) || (op->exec_time > 0)
1255 || (op->queue_time > 0)) {
1256
1257 crm_trace("Timing data (" PCMK__OP_FMT "): "
1258 "last=%lld change=%lld exec=%u queue=%u",
1259 op->rsc_id, op->op_type, op->interval_ms,
1260 (long long) op->t_run, (long long) op->t_rcchange,
1261 op->exec_time, op->queue_time);
1262
1263 if ((op->interval_ms > 0) && (op->t_rcchange > 0)) {
1264 // Recurring ops may have changed rc after initial run
1266 (long long) op->t_rcchange);
1267 } else {
1269 (long long) op->t_run);
1270 }
1271
1274 }
1275
1277 PCMK_ACTION_MIGRATE_FROM, NULL)) {
1278 /* Record PCMK__META_MIGRATE_SOURCE and PCMK__META_MIGRATE_TARGET always
1279 * for migrate ops.
1280 */
1281 const char *name = PCMK__META_MIGRATE_SOURCE;
1282
1283 crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1284
1286 crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1287 }
1288
1289 add_op_digest_to_xml(op, xml_op);
1290
1291 if (op_id_additional) {
1292 free(op_id);
1293 op_id = op_id_additional;
1294 op_id_additional = NULL;
1295 goto again;
1296 }
1297
1298 if (local_user_data) {
1299 free(local_user_data);
1300 op->user_data = NULL;
1301 }
1302 free(magic);
1303 free(op_id);
1304 free(key);
1305 return xml_op;
1306}
1307
1322bool
1324{
1325 // Only resource actions taking place on resource's lock node are locked
1326 if ((action == NULL) || (action->rsc == NULL)
1327 || !pcmk__same_node(action->node, action->rsc->priv->lock_node)) {
1328 return false;
1329 }
1330
1331 /* During shutdown, only stops are locked (otherwise, another action such as
1332 * a demote would cause the controller to clear the lock)
1333 */
1334 if (action->node->details->shutdown && (action->task != NULL)
1335 && (strcmp(action->task, PCMK_ACTION_STOP) != 0)) {
1336 return false;
1337 }
1338
1339 return true;
1340}
1341
1342/* lowest to highest */
1343static gint
1344sort_action_id(gconstpointer a, gconstpointer b)
1345{
1346 const pcmk__related_action_t *action_wrapper2 = a;
1347 const pcmk__related_action_t *action_wrapper1 = b;
1348
1349 if (a == NULL) {
1350 return 1;
1351 }
1352 if (b == NULL) {
1353 return -1;
1354 }
1355 if (action_wrapper1->action->id < action_wrapper2->action->id) {
1356 return 1;
1357 }
1358 if (action_wrapper1->action->id > action_wrapper2->action->id) {
1359 return -1;
1360 }
1361 return 0;
1362}
1363
1370void
1372{
1373 GList *item = NULL;
1374 GList *next = NULL;
1375 pcmk__related_action_t *last_input = NULL;
1376
1377 action->actions_before = g_list_sort(action->actions_before,
1378 sort_action_id);
1379 for (item = action->actions_before; item != NULL; item = next) {
1380 pcmk__related_action_t *input = item->data;
1381
1382 next = item->next;
1383 if ((last_input != NULL)
1384 && (input->action->id == last_input->action->id)) {
1385 crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1386 input->action->uuid, input->action->id,
1387 action->uuid, action->id);
1388
1389 /* For the purposes of scheduling, the ordering flags no longer
1390 * matter, but crm_simulate looks at certain ones when creating a
1391 * dot graph. Combining the flags is sufficient for that purpose.
1392 */
1393 pcmk__set_relation_flags(last_input->flags, input->flags);
1394 if (input->graphed) {
1395 last_input->graphed = true;
1396 }
1397
1398 free(item->data);
1399 action->actions_before = g_list_delete_link(action->actions_before,
1400 item);
1401 } else {
1402 last_input = input;
1403 input->graphed = false;
1404 }
1405 }
1406}
1407
1414void
1416{
1418
1419 // Output node (non-resource) actions
1420 for (GList *iter = scheduler->priv->actions;
1421 iter != NULL; iter = iter->next) {
1422
1423 char *node_name = NULL;
1424 char *task = NULL;
1425 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1426
1427 if (action->rsc != NULL) {
1428 continue; // Resource actions will be output later
1429
1430 } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
1431 continue; // This action was not scheduled
1432 }
1433
1434 if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN,
1435 pcmk__str_none)) {
1436 task = strdup("Shutdown");
1437
1438 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
1439 pcmk__str_none)) {
1440 const char *op = g_hash_table_lookup(action->meta,
1442
1443 task = crm_strdup_printf("Fence (%s)", op);
1444
1445 } else {
1446 continue; // Don't display other node action types
1447 }
1448
1449 if (pcmk__is_guest_or_bundle_node(action->node)) {
1450 const pcmk_resource_t *remote = action->node->priv->remote;
1451
1452 node_name = crm_strdup_printf("%s (resource: %s)",
1453 pcmk__node_name(action->node),
1454 remote->priv->launcher->id);
1455 } else if (action->node != NULL) {
1456 node_name = crm_strdup_printf("%s", pcmk__node_name(action->node));
1457 }
1458
1459 out->message(out, "node-action", task, node_name, action->reason);
1460
1461 free(node_name);
1462 free(task);
1463 }
1464
1465 // Output resource actions
1466 for (GList *iter = scheduler->priv->resources;
1467 iter != NULL; iter = iter->next) {
1468
1469 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1470
1471 rsc->priv->cmds->output_actions(rsc);
1472 }
1473}
1474
1484static const char *
1485task_for_digest(const char *task, guint interval_ms)
1486{
1487 /* Certain actions need to be compared against the parameters used to start
1488 * the resource.
1489 */
1490 if ((interval_ms == 0)
1492 PCMK_ACTION_PROMOTE, NULL)) {
1493 task = PCMK_ACTION_START;
1494 }
1495 return task;
1496}
1497
1515static bool
1516only_sanitized_changed(const xmlNode *xml_op,
1517 const pcmk__op_digest_t *digest_data,
1519{
1520 const char *digest_secure = NULL;
1521
1523 // The scheduler is not being run as a simulation
1524 return false;
1525 }
1526
1527 digest_secure = crm_element_value(xml_op, PCMK__XA_OP_SECURE_DIGEST);
1528
1529 return (digest_data->rc != pcmk__digest_match) && (digest_secure != NULL)
1530 && (digest_data->digest_secure_calc != NULL)
1531 && (strcmp(digest_data->digest_secure_calc, digest_secure) == 0);
1532}
1533
1543static void
1544force_restart(pcmk_resource_t *rsc, const char *task, guint interval_ms,
1545 pcmk_node_t *node)
1546{
1547 char *key = pcmk__op_key(rsc->id, task, interval_ms);
1548 pcmk_action_t *required = custom_action(rsc, key, task, NULL, FALSE,
1549 rsc->priv->scheduler);
1550
1551 pe_action_set_reason(required, "resource definition change", true);
1552 trigger_unfencing(rsc, node, "Device parameters changed", NULL,
1553 rsc->priv->scheduler);
1554}
1555
1563static void
1564schedule_reload(gpointer data, gpointer user_data)
1565{
1566 pcmk_resource_t *rsc = data;
1567 const pcmk_node_t *node = user_data;
1568
1569 pcmk_action_t *reload = NULL;
1570
1571 // For collective resources, just call recursively for children
1573 g_list_foreach(rsc->priv->children, schedule_reload, user_data);
1574 return;
1575 }
1576
1577 // Skip the reload in certain situations
1578 if ((node == NULL)
1581 pcmk__rsc_trace(rsc, "Skip reload of %s:%s%s %s",
1582 rsc->id,
1583 pcmk_is_set(rsc->flags, pcmk__rsc_managed)? "" : " unmanaged",
1584 pcmk_is_set(rsc->flags, pcmk__rsc_failed)? " failed" : "",
1585 (node == NULL)? "inactive" : node->priv->name);
1586 return;
1587 }
1588
1589 /* If a resource's configuration changed while a start was pending,
1590 * force a full restart instead of a reload.
1591 */
1593 pcmk__rsc_trace(rsc,
1594 "%s: preventing agent reload because start pending",
1595 rsc->id);
1596 custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
1597 rsc->priv->scheduler);
1598 return;
1599 }
1600
1601 // Schedule the reload
1603 reload = custom_action(rsc, reload_key(rsc), PCMK_ACTION_RELOAD_AGENT, node,
1604 FALSE, rsc->priv->scheduler);
1605 pe_action_set_reason(reload, "resource definition change", FALSE);
1606
1607 // Set orderings so that a required stop or demote cancels the reload
1608 pcmk__new_ordering(NULL, NULL, reload, rsc, stop_key(rsc), NULL,
1610 rsc->priv->scheduler);
1611 pcmk__new_ordering(NULL, NULL, reload, rsc, demote_key(rsc), NULL,
1613 rsc->priv->scheduler);
1614}
1615
1630bool
1632 const xmlNode *xml_op)
1633{
1634 guint interval_ms = 0;
1635 const char *task = NULL;
1636 const pcmk__op_digest_t *digest_data = NULL;
1637
1638 CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL),
1639 return false);
1640
1641 task = crm_element_value(xml_op, PCMK_XA_OPERATION);
1642 CRM_CHECK(task != NULL, return false);
1643
1644 crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
1645
1646 // If this is a recurring action, check whether it has been orphaned
1647 if (interval_ms > 0) {
1648 if (pcmk__find_action_config(rsc, task, interval_ms, false) != NULL) {
1649 pcmk__rsc_trace(rsc,
1650 "%s-interval %s for %s on %s is in configuration",
1651 pcmk__readable_interval(interval_ms), task, rsc->id,
1652 pcmk__node_name(node));
1653 } else if (pcmk_is_set(rsc->priv->scheduler->flags,
1657 task, interval_ms, node, "orphan");
1658 return true;
1659 } else {
1660 pcmk__rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned",
1661 pcmk__readable_interval(interval_ms), task, rsc->id,
1662 pcmk__node_name(node));
1663 return true;
1664 }
1665 }
1666
1667 crm_trace("Checking %s-interval %s for %s on %s for configuration changes",
1668 pcmk__readable_interval(interval_ms), task, rsc->id,
1669 pcmk__node_name(node));
1670 task = task_for_digest(task, interval_ms);
1671 digest_data = rsc_action_digest_cmp(rsc, xml_op, node,
1672 rsc->priv->scheduler);
1673
1674 if (only_sanitized_changed(xml_op, digest_data, rsc->priv->scheduler)) {
1675 if (!pcmk__is_daemon && (rsc->priv->scheduler->priv->out != NULL)) {
1676 pcmk__output_t *out = rsc->priv->scheduler->priv->out;
1677
1678 out->info(out,
1679 "Only 'private' parameters to %s-interval %s for %s "
1680 "on %s changed: %s",
1681 pcmk__readable_interval(interval_ms), task, rsc->id,
1682 pcmk__node_name(node),
1684 }
1685 return false;
1686 }
1687
1688 switch (digest_data->rc) {
1690 crm_log_xml_debug(digest_data->params_restart, "params:restart");
1691 force_restart(rsc, task, interval_ms, node);
1692 return true;
1693
1696 // Changes that can potentially be handled by an agent reload
1697
1698 if (interval_ms > 0) {
1699 /* Recurring actions aren't reloaded per se, they are just
1700 * re-scheduled so the next run uses the new parameters.
1701 * The old instance will be cancelled automatically.
1702 */
1703 crm_log_xml_debug(digest_data->params_all, "params:reschedule");
1704 pcmk__reschedule_recurring(rsc, task, interval_ms, node);
1705
1706 } else if (crm_element_value(xml_op,
1707 PCMK__XA_OP_RESTART_DIGEST) != NULL) {
1708 // Agent supports reload, so use it
1709 trigger_unfencing(rsc, node,
1710 "Device parameters changed (reload)", NULL,
1711 rsc->priv->scheduler);
1712 crm_log_xml_debug(digest_data->params_all, "params:reload");
1713 schedule_reload((gpointer) rsc, (gpointer) node);
1714
1715 } else {
1716 pcmk__rsc_trace(rsc,
1717 "Restarting %s "
1718 "because agent doesn't support reload",
1719 rsc->id);
1720 crm_log_xml_debug(digest_data->params_restart,
1721 "params:restart");
1722 force_restart(rsc, task, interval_ms, node);
1723 }
1724 return true;
1725
1726 default:
1727 break;
1728 }
1729 return false;
1730}
1731
1740static GList *
1741rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index)
1742{
1743 GList *ops = NULL;
1744
1745 for (xmlNode *rsc_op = pcmk__xe_first_child(rsc_entry, PCMK__XE_LRM_RSC_OP,
1746 NULL, NULL);
1747 rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op, PCMK__XE_LRM_RSC_OP)) {
1748
1749 ops = g_list_prepend(ops, rsc_op);
1750 }
1751 ops = g_list_sort(ops, sort_op_by_callid);
1752 calculate_active_ops(ops, start_index, stop_index);
1753 return ops;
1754}
1755
1770static void
1771process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc,
1772 pcmk_node_t *node)
1773{
1774 int offset = -1;
1775 int stop_index = 0;
1776 int start_index = 0;
1777 GList *sorted_op_list = NULL;
1778
1779 if (pcmk_is_set(rsc->flags, pcmk__rsc_removed)) {
1780 if (pcmk__is_anonymous_clone(pe__const_top_resource(rsc, false))) {
1781 /* @TODO Should this be done for bundled primitives as well? Added
1782 * by 2ac43ae31
1783 */
1784 pcmk__rsc_trace(rsc,
1785 "Skipping configuration check "
1786 "for orphaned clone instance %s",
1787 rsc->id);
1788 } else {
1789 pcmk__rsc_trace(rsc,
1790 "Skipping configuration check and scheduling "
1791 "clean-up for orphaned resource %s", rsc->id);
1792 pcmk__schedule_cleanup(rsc, node, false);
1793 }
1794 return;
1795 }
1796
1798 node->priv->id) == NULL) {
1799 if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) {
1800 pcmk__schedule_cleanup(rsc, node, false);
1801 }
1802 pcmk__rsc_trace(rsc,
1803 "Skipping configuration check for %s "
1804 "because no longer active on %s",
1805 rsc->id, pcmk__node_name(node));
1806 return;
1807 }
1808
1809 pcmk__rsc_trace(rsc, "Checking for configuration changes for %s on %s",
1810 rsc->id, pcmk__node_name(node));
1811
1812 if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) {
1813 pcmk__schedule_cleanup(rsc, node, false);
1814 }
1815
1816 sorted_op_list = rsc_history_as_list(rsc_entry, &start_index, &stop_index);
1817 if (start_index < stop_index) {
1818 return; // Resource is stopped
1819 }
1820
1821 for (GList *iter = sorted_op_list; iter != NULL; iter = iter->next) {
1822 xmlNode *rsc_op = (xmlNode *) iter->data;
1823 const char *task = NULL;
1824 guint interval_ms = 0;
1825
1826 if (++offset < start_index) {
1827 // Skip actions that happened before a start
1828 continue;
1829 }
1830
1831 task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
1832 crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms);
1833
1834 if ((interval_ms > 0)
1836 || node->details->maintenance)) {
1837 // Maintenance mode cancels recurring operations
1840 task, interval_ms, node, "maintenance mode");
1841
1842 } else if ((interval_ms > 0)
1846 PCMK_ACTION_MIGRATE_FROM, NULL)) {
1847 /* If a resource operation failed, and the operation's definition
1848 * has changed, clear any fail count so they can be retried fresh.
1849 */
1850
1852 /* We haven't assigned resources to nodes yet, so if the
1853 * REMOTE_CONTAINER_HACK is used, we may calculate the digest
1854 * based on the literal "#uname" value rather than the properly
1855 * substituted value. That would mistakenly make the action
1856 * definition appear to have been changed. Defer the check until
1857 * later in this case.
1858 */
1859 pcmk__add_param_check(rsc_op, rsc, node, pcmk__check_active);
1860
1861 } else if (pcmk__check_action_config(rsc, node, rsc_op)
1862 && (pe_get_failcount(node, rsc, NULL, pcmk__fc_effective,
1863 NULL) != 0)) {
1864 pe__clear_failcount(rsc, node, "action definition changed",
1865 rsc->priv->scheduler);
1866 }
1867 }
1868 }
1869 g_list_free(sorted_op_list);
1870}
1871
1885static void
1886process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs)
1887{
1888 crm_trace("Processing node history for %s", pcmk__node_name(node));
1889 for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rscs,
1891 NULL, NULL);
1892 rsc_entry != NULL;
1893 rsc_entry = pcmk__xe_next(rsc_entry, PCMK__XE_LRM_RESOURCE)) {
1894
1895 if (rsc_entry->children != NULL) {
1896 GList *result = pcmk__rscs_matching_id(pcmk__xe_id(rsc_entry),
1897 node->priv->scheduler);
1898
1899 for (GList *iter = result; iter != NULL; iter = iter->next) {
1900 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1901
1902 if (pcmk__is_primitive(rsc)) {
1903 process_rsc_history(rsc_entry, rsc, node);
1904 }
1905 }
1906 g_list_free(result);
1907 }
1908 }
1909}
1910
1911// XPath to find a node's resource history
1912#define XPATH_NODE_HISTORY "/" PCMK_XE_CIB "/" PCMK_XE_STATUS \
1913 "/" PCMK__XE_NODE_STATE \
1914 "[@" PCMK_XA_UNAME "='%s']" \
1915 "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES
1916
1929void
1931{
1932 crm_trace("Check resource and action configuration for changes");
1933
1934 /* Rather than iterate through the status section, iterate through the nodes
1935 * and search for the appropriate status subsection for each. This skips
1936 * orphaned nodes and lets us eliminate some cases before searching the XML.
1937 */
1938 for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
1939 pcmk_node_t *node = (pcmk_node_t *) iter->data;
1940
1941 /* Don't bother checking actions for a node that can't run actions ...
1942 * unless it's in maintenance mode, in which case we still need to
1943 * cancel any existing recurring monitors.
1944 */
1945 if (node->details->maintenance
1946 || pcmk__node_available(node, false, false)) {
1947
1948 char *xpath = NULL;
1949 xmlNode *history = NULL;
1950
1952 history = pcmk__xpath_find_one(scheduler->input->doc, xpath,
1953 LOG_NEVER);
1954 free(xpath);
1955
1956 process_node_history(node, history);
1957 }
1958 }
1959}
@ pcmk__ar_if_on_same_node
Relation applies only if actions are on same node.
@ pcmk__ar_first_else_then
If 'first' is unrunnable, 'then' becomes a real, unmigratable action.
@ pcmk__ar_first_implies_then
@ pcmk__ar_asymmetric
User-configured asymmetric ordering.
@ pcmk__ar_first_implies_same_node_then
If 'first' is required, 'then' action for instance on same node is.
@ pcmk__ar_then_implies_first
@ pcmk__ar_intermediate_stop
@ pcmk__ar_promoted_then_implies_first
@ pcmk__ar_then_implies_first_graphed
If 'then' is required, 'first' must be added to the transition graph.
@ pcmk__ar_min_runnable
'then' action is runnable if certain number of 'first' instances are
@ pcmk__ar_none
No relation (compare with equality rather than bit set)
@ pcmk__ar_first_implies_then_graphed
If 'first' is required and runnable, 'then' must be in graph.
@ pcmk__ar_nested_remote_probe
@ pcmk__ar_unrunnable_first_blocks
'then' is runnable (and migratable) only if 'first' is runnable
@ pcmk__ar_unmigratable_then_blocks
@ pcmk__ar_ordered
Actions are ordered (optionally, if no other flags are set)
@ pcmk__ar_then_cancels_first
If 'then' action becomes required, 'first' becomes optional.
#define pcmk__clear_relation_flags(ar_flags, flags_to_clear)
#define pcmk__set_relation_flags(ar_flags, flags_to_set)
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:278
gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc)
Definition actions.c:512
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_PROMOTE
Definition actions.h:57
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:49
#define PCMK_ACTION_RELOAD
Definition actions.h:60
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:50
#define PCMK_ACTION_MONITOR
Definition actions.h:51
#define PCMK_ACTION_STONITH
Definition actions.h:65
#define PCMK_ACTION_RELOAD_AGENT
Definition actions.h:61
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:42
#define PCMK_ACTION_NOTIFY
Definition actions.h:53
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition digest.c:299
pcmk__action_flags
@ pcmk__action_always_in_graph
@ pcmk__action_runnable
@ pcmk__action_migratable
@ pcmk__action_optional
@ pcmk__action_pseudo
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:365
#define pcmk__set_action_flags(action, flags_to_set)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition actions.c:225
const char * pcmk__action_text(enum pcmk__action_type action)
Definition actions.c:34
#define pcmk__clear_action_flags(action, flags_to_clear)
enum pcmk__action_type pcmk__parse_action(const char *action_name)
Definition actions.c:90
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition actions.c:432
#define pcmk__set_raw_action_flags(action_flags, action_name, to_set)
pcmk__action_type
@ pcmk__action_stop
@ pcmk__action_started
@ pcmk__action_monitor
@ pcmk__action_fence
@ pcmk__action_stopped
@ pcmk__action_notify
@ pcmk__action_start
@ pcmk__action_demote
@ pcmk__action_promote
@ pcmk__action_shutdown
@ pcmk__action_notified
@ pcmk__action_unspecified
@ pcmk__action_promoted
@ pcmk__action_demoted
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
const char * parent
Definition cib.c:27
const char * name
Definition cib.c:26
bool pcmk__is_daemon
Definition logging.c:47
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
gboolean is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
Definition complex.c:982
char data[0]
Definition cpg.c:10
enum pcmk_ipc_server type
Definition cpg.c:3
@ pcmk__digest_match
@ pcmk__digest_restart
@ pcmk__digest_mismatch
@ pcmk__digest_unknown
char * pcmk__digest_operation(xmlNode *input)
Definition digest.c:133
@ pcmk__fc_effective
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:2210
G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pcmk_action_t *action)
G_GNUC_INTERNAL void pcmk__schedule_cancel(pcmk_resource_t *rsc, const char *call_id, const char *task, guint interval_ms, const pcmk_node_t *node, const char *reason)
G_GNUC_INTERNAL void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_task, pcmk_action_t *first_action, pcmk_resource_t *then_rsc, char *then_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched)
#define pcmk__set_updated_flags(au_flags, action, flags_to_set)
G_GNUC_INTERNAL GList * pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL void pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op)
G_GNUC_INTERNAL void pcmk__schedule_cleanup(pcmk_resource_t *rsc, const pcmk_node_t *node, bool optional)
#define pcmk__clear_updated_flags(au_flags, action, flags_to_clear)
@ pcmk__updated_none
@ pcmk__updated_first
@ pcmk__updated_then
G_GNUC_INTERNAL void pcmk__reschedule_recurring(pcmk_resource_t *rsc, const char *task, guint interval_ms, pcmk_node_t *node)
G_GNUC_INTERNAL bool pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *rsc_entry, bool active_on_node)
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
#define crm_warn(fmt, args...)
Definition logging.h:360
#define crm_log_xml_debug(xml, text)
Definition logging.h:377
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define LOG_NEVER
Definition logging.h:48
#define crm_trace(fmt, args...)
Definition logging.h:370
void lrmd__set_result(lrmd_event_data_t *event, enum ocf_exitcode rc, int op_status, const char *exit_reason)
pcmk_scheduler_t * scheduler
xmlNode * input
const char * crm_meta_value(GHashTable *hash, const char *field)
Get the value of a meta-attribute.
Definition nvpair.c:553
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:238
#define pcmk__insert_meta(obj, name, value)
#define PCMK_META_INTERVAL
Definition options.h:92
#define PCMK_VALUE_TRUE
Definition options.h:219
#define PCMK__META_MIGRATE_SOURCE
#define PCMK__META_ON_NODE
#define PCMK__META_MIGRATE_TARGET
#define PCMK__META_OP_NO_WAIT
#define PCMK__META_STONITH_ACTION
const char * action
Definition pcmk_fence.c:32
pcmk__action_result_t result
Definition pcmk_fence.c:37
const char * target
Definition pcmk_fence.c:31
void pcmk__log_action(const char *pre_text, const pcmk_action_t *action, bool details)
#define XPATH_NODE_HISTORY
pcmk_action_t * pcmk__new_shutdown_action(pcmk_node_t *node)
xmlNode * pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op, const char *caller_version, int target_rc, const char *node, const char *origin)
#define action_type_str(flags)
void pcmk__update_action_for_orderings(pcmk_action_t *then, pcmk_scheduler_t *scheduler)
void pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler)
#define action_node_str(a)
#define action_runnable_str(flags)
void pcmk__output_actions(pcmk_scheduler_t *scheduler)
#define clear_action_flag_because(action, flag, reason)
bool pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node, const xmlNode *xml_op)
uint32_t pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
#define FAKE_TE_ID
bool pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
#define action_optional_str(flags)
void pcmk__deduplicate_action_inputs(pcmk_action_t *action)
pcmk_action_t * pe__clear_failcount(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *reason, pcmk_scheduler_t *scheduler)
Schedule a controller operation to clear a fail count.
Definition failcounts.c:467
#define demote_key(rsc)
Definition internal.h:194
#define reload_key(rsc)
Definition internal.h:191
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition complex.c:1025
pcmk__op_digest_t * rsc_action_digest_cmp(pcmk_resource_t *rsc, const xmlNode *xml_op, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:391
gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags)
Definition utils.c:483
bool pe__rsc_running_on_only(const pcmk_resource_t *rsc, const pcmk_node_t *node)
Definition utils.c:759
#define stop_key(rsc)
Definition internal.h:190
int pe_get_failcount(const pcmk_node_t *node, pcmk_resource_t *rsc, time_t *last_failure, uint32_t flags, const xmlNode *xml_op)
Definition failcounts.c:367
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
bool pe__bundle_needs_remote_name(pcmk_resource_t *rsc)
Definition bundle.c:893
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
pcmk_action_t * custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler)
Create or update an action object.
void trigger_unfencing(pcmk_resource_t *rsc, pcmk_node_t *node, const char *reason, pcmk_action_t *dependency, pcmk_scheduler_t *scheduler)
Definition utils.c:628
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition pe_actions.c:137
@ pcmk__rsc_managed
@ pcmk__rsc_maintenance
@ pcmk__rsc_blocked
@ pcmk__rsc_reload
@ pcmk__rsc_removed
@ pcmk__rsc_notify
@ pcmk__rsc_start_pending
@ pcmk__rsc_failed
#define pcmk__set_rsc_flags(resource, flags_to_set)
@ pcmk__rsc_variant_group
Group resource.
@ pcmk__rsc_variant_primitive
Primitive resource.
@ pcmk__rsc_node_assigned
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
@ PCMK_OCF_OK
Success.
Definition results.h:174
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition results.h:311
@ PCMK_EXEC_PENDING
Action is in progress.
Definition results.h:310
#define pcmk__assert(expr)
rsc_role_e
Definition roles.h:34
@ pcmk_role_started
Started.
Definition roles.h:37
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
@ pcmk_role_stopped
Stopped.
Definition roles.h:36
#define pcmk__rsc_trace(rsc, fmt, args...)
@ pcmk__check_active
#define pcmk__rsc_debug(rsc, fmt, args...)
@ pcmk__sched_sanitized
@ pcmk__sched_cancel_removed_actions
void pcmk__add_param_check(const xmlNode *rsc_op, pcmk_resource_t *rsc, pcmk_node_t *node, enum pcmk__check_parameters)
Definition scheduler.c:360
void calculate_active_ops(const GList *sorted_op_list, int *start_index, int *stop_index)
Definition unpack.c:2641
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition status.c:248
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_none
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
#define pcmk__str_copy(str)
const char * op_type
Definition lrmd_events.h:45
const char * exit_reason
Definition lrmd_events.h:98
const char * user_data
Definition lrmd_events.h:47
unsigned int exec_time
Definition lrmd_events.h:80
enum ocf_exitcode rc
Definition lrmd_events.h:65
unsigned int queue_time
Definition lrmd_events.h:83
const char * rsc_id
Definition lrmd_events.h:43
pcmk_node_t * node
pcmk_resource_t * rsc
uint32_t(* update_ordered_actions)(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
void(* output_actions)(pcmk_resource_t *rsc)
gboolean maintenance
Definition nodes.h:66
pcmk_scheduler_t * scheduler
enum pcmk__digest_result rc
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
pcmk_scheduler_t * scheduler
enum pcmk__rsc_variant variant
const pcmk__assignment_methods_t * cmds
const pcmk__rsc_methods_t * fns
unsigned long long flags
Definition resources.h:69
pcmk__resource_private_t * priv
Definition resources.h:61
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, bool current)
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, uint32_t target)
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
GList * nodes
Definition scheduler.h:97
xmlNode * input
Definition scheduler.h:81
uint64_t flags
Definition scheduler.h:89
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_details * details
Definition nodes.h:82
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
const char * crm_xml_add_ll(xmlNode *node, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
const char * crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
Create an XML attribute with specified name and unsigned value.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XA_QUEUE_TIME
Definition xml_names.h:365
#define PCMK_XA_OPERATION
Definition xml_names.h:349
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_LAST_RC_CHANGE
Definition xml_names.h:316
#define PCMK_XA_EXEC_TIME
Definition xml_names.h:270
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:254
#define PCMK_XE_PARAMETERS
Definition xml_names.h:159
#define PCMK_XA_EXIT_REASON
Definition xml_names.h:274
#define PCMK_XA_CRM_DEBUG_ORIGIN
Definition xml_names.h:253
#define PCMK__XE_LRM_RSC_OP
#define PCMK__XE_LRM_RESOURCE
#define PCMK__XA_OP_DIGEST
#define PCMK__XA_CALL_ID
#define PCMK__XA_TRANSITION_MAGIC
#define PCMK__XA_OP_RESTART_DIGEST
#define PCMK__XA_OPERATION_KEY
#define PCMK__XA_OP_STATUS
#define PCMK__XA_OP_SECURE_DIGEST
#define PCMK__XA_TRANSITION_KEY
#define PCMK__XA_RC_CODE
xmlNode * pcmk__xpath_find_one(xmlDoc *doc, const char *path, uint8_t level)
Definition xpath.c:206