pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
pcmk_sched_actions.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 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 <stdio.h>
13#include <sys/param.h>
14#include <glib.h>
15
16#include <crm/lrmd_internal.h>
17#include <pacemaker-internal.h>
19
30static enum pe_action_flags
31action_flags_for_ordering(pe_action_t *action, pe_node_t *node)
32{
33 bool runnable = false;
35
36 // For non-resource actions, return the action flags
37 if (action->rsc == NULL) {
38 return action->flags;
39 }
40
41 /* For non-clone resources, or a clone action not assigned to a node,
42 * return the flags as determined by the resource method without a node
43 * specified.
44 */
45 flags = action->rsc->cmds->action_flags(action, NULL);
46 if ((node == NULL) || !pe_rsc_is_clone(action->rsc)) {
47 return flags;
48 }
49
50 /* Otherwise (i.e., for clone resource actions on a specific node), first
51 * remember whether the non-node-specific action is runnable.
52 */
54
55 // Then recheck the resource method with the node
56 flags = action->rsc->cmds->action_flags(action, node);
57
58 /* For clones in ordering constraints, the node-specific "runnable" doesn't
59 * matter, just the non-node-specific setting (i.e., is the action runnable
60 * anywhere).
61 *
62 * This applies only to runnable, and only for ordering constraints. This
63 * function shouldn't be used for other types of constraints without
64 * changes. Not very satisfying, but it's logical and appears to work well.
65 */
66 if (runnable && !pcmk_is_set(flags, pe_action_runnable)) {
69 }
70 return flags;
71}
72
91static char *
92action_uuid_for_ordering(const char *first_uuid, pe_resource_t *first_rsc)
93{
94 guint interval_ms = 0;
95 char *uuid = NULL;
96 char *rid = NULL;
97 char *first_task_str = NULL;
98 enum action_tasks first_task = no_action;
99 enum action_tasks remapped_task = no_action;
100
101 // Only non-notify actions for collective resources need remapping
102 if ((strstr(first_uuid, "notify") != NULL)
103 || (first_rsc->variant < pe_group)) {
104 goto done;
105 }
106
107 // Only non-recurring actions need remapping
108 CRM_ASSERT(parse_op_key(first_uuid, &rid, &first_task_str, &interval_ms));
109 if (interval_ms > 0) {
110 goto done;
111 }
112
113 first_task = text2task(first_task_str);
114 switch (first_task) {
115 case stop_rsc:
116 case start_rsc:
117 case action_notify:
118 case action_promote:
119 case action_demote:
120 remapped_task = first_task + 1;
121 break;
122 case stopped_rsc:
123 case started_rsc:
124 case action_notified:
125 case action_promoted:
126 case action_demoted:
127 remapped_task = first_task;
128 break;
129 case monitor_rsc:
130 case shutdown_crm:
131 case stonith_node:
132 break;
133 default:
134 crm_err("Unknown action '%s' in ordering", first_task_str);
135 break;
136 }
137
138 if (remapped_task != no_action) {
139 /* If a (clone) resource has notifications enabled, we want to order
140 * relative to when all notifications have been sent for the remapped
141 * task. Only outermost resources or those in bundles have
142 * notifications.
143 */
144 if (pcmk_is_set(first_rsc->flags, pe_rsc_notify)
145 && ((first_rsc->parent == NULL)
146 || (pe_rsc_is_clone(first_rsc)
147 && (first_rsc->parent->variant == pe_container)))) {
148 uuid = pcmk__notify_key(rid, "confirmed-post",
149 task2text(remapped_task));
150 } else {
151 uuid = pcmk__op_key(rid, task2text(remapped_task), 0);
152 }
153 pe_rsc_trace(first_rsc,
154 "Remapped action UUID %s to %s for ordering purposes",
155 first_uuid, uuid);
156 }
157
158done:
159 if (uuid == NULL) {
160 uuid = strdup(first_uuid);
161 CRM_ASSERT(uuid != NULL);
162 }
163 free(first_task_str);
164 free(rid);
165 return uuid;
166}
167
184static pe_action_t *
185action_for_ordering(pe_action_t *action)
186{
188 pe_resource_t *rsc = action->rsc;
189
190 if ((rsc != NULL) && (rsc->variant >= pe_group) && (action->uuid != NULL)) {
191 char *uuid = action_uuid_for_ordering(action->uuid, rsc);
192
193 result = find_first_action(rsc->actions, uuid, NULL, NULL);
194 if (result == NULL) {
195 crm_warn("Not remapping %s to %s because %s does not have "
196 "remapped action", action->uuid, uuid, rsc->id);
197 result = action;
198 }
199 free(uuid);
200 }
201 return result;
202}
203
217static uint32_t
218update_action_for_ordering_flags(pe_action_t *first, pe_action_t *then,
219 enum pe_action_flags first_flags,
220 enum pe_action_flags then_flags,
221 pe_action_wrapper_t *order,
223{
224 uint32_t changed = pcmk__updated_none;
225
226 /* The node will only be used for clones. If interleaved, node will be NULL,
227 * otherwise the ordering scope will be limited to the node. Normally, the
228 * whole 'then' clone should restart if 'first' is restarted, so then->node
229 * is needed.
230 */
231 pe_node_t *node = then->node;
232
234 /* For unfencing, only instances of 'then' on the same node as 'first'
235 * (the unfencing operation) should restart, so reset node to
236 * first->node, at which point this case is handled like a normal
237 * pe_order_implies_then.
238 */
241 node = first->node;
242 pe_rsc_trace(then->rsc,
243 "%s then %s: mapped pe_order_implies_then_on_node to "
244 "pe_order_implies_then on %s",
245 first->uuid, then->uuid, pe__node_name(node));
246 }
247
249 if (then->rsc != NULL) {
250 changed |= then->rsc->cmds->update_ordered_actions(first, then,
251 node,
252 first_flags & pe_action_optional,
255 data_set);
256 } else if (!pcmk_is_set(first_flags, pe_action_optional)
260 }
261 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_then",
262 first->uuid, then->uuid,
263 (changed? "changed" : "unchanged"));
264 }
265
266 if (pcmk_is_set(order->type, pe_order_restart) && (then->rsc != NULL)) {
268
269 changed |= then->rsc->cmds->update_ordered_actions(first, then, node,
270 first_flags, restart,
272 data_set);
273 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_restart",
274 first->uuid, then->uuid,
275 (changed? "changed" : "unchanged"));
276 }
277
279 if (first->rsc != NULL) {
280 changed |= first->rsc->cmds->update_ordered_actions(first, then,
281 node,
282 first_flags,
285 data_set);
286 } else if (!pcmk_is_set(first_flags, pe_action_optional)
290 }
291 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_first",
292 first->uuid, then->uuid,
293 (changed? "changed" : "unchanged"));
294 }
295
297 if (then->rsc != NULL) {
298 changed |= then->rsc->cmds->update_ordered_actions(first, then,
299 node,
300 first_flags & pe_action_optional,
303 data_set);
304 }
305 pe_rsc_trace(then->rsc,
306 "%s then %s: %s after pe_order_promoted_implies_first",
307 first->uuid, then->uuid,
308 (changed? "changed" : "unchanged"));
309 }
310
312 if (then->rsc != NULL) {
313 changed |= then->rsc->cmds->update_ordered_actions(first, then,
314 node,
315 first_flags,
318 data_set);
319
320 } else if (pcmk_is_set(first_flags, pe_action_runnable)) {
321 // We have another runnable instance of "first"
322 then->runnable_before++;
323
324 /* Mark "then" as runnable if it requires a certain number of
325 * "before" instances to be runnable, and they now are.
326 */
327 if ((then->runnable_before >= then->required_runnable_before)
329
332 }
333 }
334 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_one_or_more",
335 first->uuid, then->uuid,
336 (changed? "changed" : "unchanged"));
337 }
338
339 if (pcmk_is_set(order->type, pe_order_probe) && (then->rsc != NULL)) {
340 if (!pcmk_is_set(first_flags, pe_action_runnable)
341 && (first->rsc->running_on != NULL)) {
342
343 pe_rsc_trace(then->rsc,
344 "%s then %s: ignoring because first is stopping",
345 first->uuid, then->uuid);
346 order->type = pe_order_none;
347 } else {
348 changed |= then->rsc->cmds->update_ordered_actions(first, then,
349 node,
350 first_flags,
353 data_set);
354 }
355 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_probe",
356 first->uuid, then->uuid,
357 (changed? "changed" : "unchanged"));
358 }
359
361 if (then->rsc != NULL) {
362 changed |= then->rsc->cmds->update_ordered_actions(first, then,
363 node,
364 first_flags,
367 data_set);
368
369 } else if (!pcmk_is_set(first_flags, pe_action_runnable)
371
374 }
375 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_runnable_left",
376 first->uuid, then->uuid,
377 (changed? "changed" : "unchanged"));
378 }
379
381 if (then->rsc != NULL) {
382 changed |= then->rsc->cmds->update_ordered_actions(first, then,
383 node,
384 first_flags,
387 data_set);
388 }
389 pe_rsc_trace(then->rsc, "%s then %s: %s after "
390 "pe_order_implies_first_migratable",
391 first->uuid, then->uuid,
392 (changed? "changed" : "unchanged"));
393 }
394
396 if (then->rsc != NULL) {
397 changed |= then->rsc->cmds->update_ordered_actions(first, then,
398 node,
399 first_flags,
402 data_set);
403 }
404 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_pseudo_left",
405 first->uuid, then->uuid,
406 (changed? "changed" : "unchanged"));
407 }
408
409 if (pcmk_is_set(order->type, pe_order_optional)) {
410 if (then->rsc != NULL) {
411 changed |= then->rsc->cmds->update_ordered_actions(first, then,
412 node,
413 first_flags,
416 data_set);
417 }
418 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_optional",
419 first->uuid, then->uuid,
420 (changed? "changed" : "unchanged"));
421 }
422
424 if (then->rsc != NULL) {
425 changed |= then->rsc->cmds->update_ordered_actions(first, then,
426 node,
427 first_flags,
430 data_set);
431 }
432 pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_asymmetrical",
433 first->uuid, then->uuid,
434 (changed? "changed" : "unchanged"));
435 }
436
439 && !pcmk_is_set(first_flags, pe_action_optional)) {
440
441 pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
442 then->uuid, first->uuid);
444 // Don't bother marking 'then' as changed just for this
445 }
446
448 && !pcmk_is_set(then_flags, pe_action_optional)) {
449
450 pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
451 first->uuid, then->uuid);
453 // Don't bother marking 'first' as changed just for this
454 }
455
456 if (pcmk_any_flags_set(order->type, pe_order_implies_then
459 && (first->rsc != NULL)
460 && !pcmk_is_set(first->rsc->flags, pe_rsc_managed)
461 && pcmk_is_set(first->rsc->flags, pe_rsc_block)
463 && pcmk__str_eq(first->task, RSC_STOP, pcmk__str_casei)) {
464
468 }
469 pe_rsc_trace(then->rsc, "%s then %s: %s after checking whether first "
470 "is blocked, unmanaged, unrunnable stop",
471 first->uuid, then->uuid,
472 (changed? "changed" : "unchanged"));
473 }
474
475 return changed;
476}
477
478// Convenience macros for logging action properties
479
480#define action_type_str(flags) \
481 (pcmk_is_set((flags), pe_action_pseudo)? "pseudo-action" : "action")
482
483#define action_optional_str(flags) \
484 (pcmk_is_set((flags), pe_action_optional)? "optional" : "required")
485
486#define action_runnable_str(flags) \
487 (pcmk_is_set((flags), pe_action_runnable)? "runnable" : "unrunnable")
488
489#define action_node_str(a) \
490 (((a)->node == NULL)? "no node" : (a)->node->details->uname)
491
499void
501{
502 GList *lpc = NULL;
503 uint32_t changed = pcmk__updated_none;
504 int last_flags = then->flags;
505
506 pe_rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
507 action_type_str(then->flags), then->uuid,
510
512 /* Initialize current known "runnable before" actions. As
513 * update_action_for_ordering_flags() is called for each of then's
514 * before actions, this number will increment as runnable 'first'
515 * actions are encountered.
516 */
517 then->runnable_before = 0;
518
519 if (then->required_runnable_before == 0) {
520 /* @COMPAT This ordering constraint uses the deprecated
521 * "require-all=false" attribute. Treat it like "clone-min=1".
522 */
523 then->required_runnable_before = 1;
524 }
525
526 /* The pe_order_one_or_more clause of update_action_for_ordering_flags()
527 * (called below) will reset runnable if appropriate.
528 */
530 }
531
532 for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
533 pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
534 pe_action_t *first = other->action;
535
536 pe_node_t *then_node = then->node;
537 pe_node_t *first_node = first->node;
538
539 if ((first->rsc != NULL)
540 && (first->rsc->variant == pe_group)
541 && pcmk__str_eq(first->task, RSC_START, pcmk__str_casei)) {
542
543 first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
544 if (first_node != NULL) {
545 pe_rsc_trace(first->rsc, "Found %s for 'first' %s",
546 pe__node_name(first_node), first->uuid);
547 }
548 }
549
550 if ((then->rsc != NULL)
551 && (then->rsc->variant == pe_group)
552 && pcmk__str_eq(then->task, RSC_START, pcmk__str_casei)) {
553
554 then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
555 if (then_node != NULL) {
556 pe_rsc_trace(then->rsc, "Found %s for 'then' %s",
557 pe__node_name(then_node), then->uuid);
558 }
559 }
560
561 // Disable constraint if it only applies when on same node, but isn't
563 && (first_node != NULL) && (then_node != NULL)
564 && (first_node->details != then_node->details)) {
565
566 pe_rsc_trace(then->rsc,
567 "Disabled ordering %s on %s then %s on %s: not same node",
568 other->action->uuid, pe__node_name(first_node),
569 then->uuid, pe__node_name(then_node));
570 other->type = pe_order_none;
571 continue;
572 }
573
575
576 if ((first->rsc != NULL)
579
580 /* 'then' is required, so we must abandon 'first'
581 * (e.g. a required stop cancels any agent reload).
582 */
584 if (!strcmp(first->task, CRMD_ACTION_RELOAD_AGENT)) {
586 }
587 }
588
589 if ((first->rsc != NULL) && (then->rsc != NULL)
590 && (first->rsc != then->rsc) && !is_parent(then->rsc, first->rsc)) {
591 first = action_for_ordering(first);
592 }
593 if (first != other->action) {
594 pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
595 then->uuid, first->uuid, other->action->uuid);
596 }
597
598 pe_rsc_trace(then->rsc,
599 "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s",
600 first->uuid, first->flags, then->uuid, then->flags,
601 other->type, action_node_str(first));
602
603 if (first == other->action) {
604 /* 'first' was not remapped (e.g. from 'start' to 'running'), which
605 * could mean it is a non-resource action, a primitive resource
606 * action, or already expanded.
607 */
608 enum pe_action_flags first_flags, then_flags;
609
610 first_flags = action_flags_for_ordering(first, then_node);
611 then_flags = action_flags_for_ordering(then, first_node);
612
613 changed |= update_action_for_ordering_flags(first, then,
614 first_flags, then_flags,
615 other, data_set);
616
617 /* 'first' was for a complex resource (clone, group, etc),
618 * create a new dependency if necessary
619 */
620 } else if (order_actions(first, then, other->type)) {
621 /* This was the first time 'first' and 'then' were associated,
622 * start again to get the new actions_before list
623 */
625 pe_rsc_trace(then->rsc,
626 "Disabled ordering %s then %s in favor of %s then %s",
627 other->action->uuid, then->uuid, first->uuid,
628 then->uuid);
629 other->type = pe_order_none;
630 }
631
632
633 if (pcmk_is_set(changed, pcmk__updated_first)) {
634 crm_trace("Re-processing %s and its 'after' actions "
635 "because it changed", first->uuid);
636 for (GList *lpc2 = first->actions_after; lpc2 != NULL;
637 lpc2 = lpc2->next) {
638 pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc2->data;
639
641 }
643 }
644 }
645
647 if (last_flags == then->flags) {
649 } else {
651 }
652 }
653
654 if (pcmk_is_set(changed, pcmk__updated_then)) {
655 crm_trace("Re-processing %s and its 'after' actions because it changed",
656 then->uuid);
657 if (pcmk_is_set(last_flags, pe_action_runnable)
660 }
662 for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
663 pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
664
666 }
667 }
668}
669
670static inline bool
671is_primitive_action(pe_action_t *action)
672{
673 return action && action->rsc && (action->rsc->variant == pe_native);
674}
675
684#define clear_action_flag_because(action, flag, reason) do { \
685 if (pcmk_is_set((action)->flags, (flag))) { \
686 pe__clear_action_flags(action, flag); \
687 if ((action)->rsc != (reason)->rsc) { \
688 char *reason_text = pe__action2reason((reason), (flag)); \
689 pe_action_set_reason((action), reason_text, \
690 ((flag) == pe_action_migrate_runnable)); \
691 free(reason_text); \
692 } \
693 } \
694 } while (0)
695
703static void
704handle_asymmetric_ordering(pe_action_t *first, pe_action_t *then)
705{
706 enum rsc_role_e then_rsc_role = RSC_ROLE_UNKNOWN;
707 GList *then_on = NULL;
708
709 if (then->rsc == NULL) {
710 // Asymmetric orderings only matter if there's a resource involved
711 return;
712 }
713
714 then_rsc_role = then->rsc->fns->state(then->rsc, TRUE);
715 then_on = then->rsc->running_on;
716
717 if ((then_rsc_role == RSC_ROLE_STOPPED)
718 && pcmk__str_eq(then->task, RSC_STOP, pcmk__str_none)) {
719 /* Nothing needs to be done for asymmetric ordering if 'then' is
720 * supposed to be stopped after 'first' but is already stopped.
721 */
722 return;
723 }
724
725 if ((then_rsc_role >= RSC_ROLE_STARTED)
727 && (then->node != NULL)
728 && pcmk__list_of_1(then_on)
729 && (then->node->details == ((pe_node_t *) then_on->data)->details)
730 && pcmk__str_eq(then->task, RSC_START, pcmk__str_none)) {
731 /* Nothing needs to be done for asymmetric ordering if 'then' is
732 * supposed to be started after 'first' but is already started --
733 * unless the start is mandatory, which indicates the resource is
734 * restarting and the ordering is still needed.
735 */
736 return;
737 }
738
739 if (!pcmk_is_set(first->flags, pe_action_runnable)) {
740 // 'First' can't run, so 'then' can't either
743 }
744}
745
757static void
758handle_restart_ordering(pe_action_t *first, pe_action_t *then, uint32_t filter)
759{
760 const char *reason = NULL;
761
762 CRM_ASSERT(is_primitive_action(first));
763 CRM_ASSERT(is_primitive_action(then));
764
765 // We need to update the action in two cases:
766
767 // ... if 'then' is required
770 reason = "restart";
771 }
772
773 /* ... if 'then' is unrunnable action on same resource (if a resource
774 * should restart but can't start, we still want to stop)
775 */
779 && (first->rsc == then->rsc)) {
780 reason = "stop";
781 }
782
783 if (reason == NULL) {
784 return;
785 }
786
787 pe_rsc_trace(first->rsc, "Handling %s -> %s for %s",
788 first->uuid, then->uuid, reason);
789
790 // Make 'first' required if it is runnable
791 if (pcmk_is_set(first->flags, pe_action_runnable)) {
793 }
794
795 // Make 'first' required if 'then' is required
796 if (!pcmk_is_set(then->flags, pe_action_optional)) {
798 }
799
800 // Make 'first' unmigratable if 'then' is unmigratable
803 }
804
805 // Make 'then' unrunnable if 'first' is required but unrunnable
807 && !pcmk_is_set(first->flags, pe_action_runnable)) {
809 }
810}
811
834uint32_t
836 const pe_node_t *node, uint32_t flags,
837 uint32_t filter, uint32_t type,
839{
840 uint32_t changed = pcmk__updated_none;
841 uint32_t then_flags = then->flags;
842 uint32_t first_flags = first->flags;
843
845 handle_asymmetric_ordering(first, then);
846 }
847
849 && !pcmk_is_set(then_flags, pe_action_optional)) {
850 // Then is required, and implies first should be, too
851
854 && pcmk_is_set(first_flags, pe_action_optional)) {
856 }
857
861 }
862 }
863
865 && (then->rsc != NULL) && (then->rsc->role == RSC_ROLE_PROMOTED)
868
870
874 then);
875 }
876 }
877
879 && pcmk_is_set(filter, pe_action_optional)) {
880
881 if (!pcmk_all_flags_set(then->flags,
884 }
885
886 if (!pcmk_is_set(then->flags, pe_action_optional)) {
888 }
889 }
890
893 && !pcmk_is_set(first->flags, pe_action_runnable)) {
894
897 }
898
903
906 }
907
913
915 }
916
918 handle_restart_ordering(first, then, filter);
919 }
920
921 if (then_flags != then->flags) {
923 pe_rsc_trace(then->rsc,
924 "%s on %s: flags are now %#.6x (was %#.6x) "
925 "because of 'first' %s (%#.6x)",
926 then->uuid, pe__node_name(then->node),
927 then->flags, then_flags, first->uuid, first->flags);
928
929 if ((then->rsc != NULL) && (then->rsc->parent != NULL)) {
930 // Required to handle "X_stop then X_start" for cloned groups
932 }
933 }
934
935 if (first_flags != first->flags) {
937 pe_rsc_trace(first->rsc,
938 "%s on %s: flags are now %#.6x (was %#.6x) "
939 "because of 'then' %s (%#.6x)",
940 first->uuid, pe__node_name(first->node),
941 first->flags, first_flags, then->uuid, then->flags);
942 }
943
944 return changed;
945}
946
955void
956pcmk__log_action(const char *pre_text, pe_action_t *action, bool details)
957{
958 const char *node_uname = NULL;
959 const char *node_uuid = NULL;
960 const char *desc = NULL;
961
962 CRM_CHECK(action != NULL, return);
963
964 if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
965 if (action->node != NULL) {
966 node_uname = action->node->details->uname;
967 node_uuid = action->node->details->id;
968 } else {
969 node_uname = "<none>";
970 }
971 }
972
973 switch (text2task(action->task)) {
974 case stonith_node:
975 case shutdown_crm:
976 if (pcmk_is_set(action->flags, pe_action_pseudo)) {
977 desc = "Pseudo ";
978 } else if (pcmk_is_set(action->flags, pe_action_optional)) {
979 desc = "Optional ";
980 } else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
981 desc = "!!Non-Startable!! ";
982 } else if (pcmk_is_set(action->flags, pe_action_processed)) {
983 desc = "";
984 } else {
985 desc = "(Provisional) ";
986 }
987 crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
988 ((pre_text == NULL)? "" : pre_text),
989 ((pre_text == NULL)? "" : ": "),
990 desc, action->id, action->uuid,
991 (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
992 (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
993 (node_uuid? ")" : ""));
994 break;
995 default:
997 desc = "Optional ";
998 } else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
999 desc = "Pseudo ";
1000 } else if (!pcmk_is_set(action->flags, pe_action_runnable)) {
1001 desc = "!!Non-Startable!! ";
1002 } else if (pcmk_is_set(action->flags, pe_action_processed)) {
1003 desc = "";
1004 } else {
1005 desc = "(Provisional) ";
1006 }
1007 crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
1008 ((pre_text == NULL)? "" : pre_text),
1009 ((pre_text == NULL)? "" : ": "),
1010 desc, action->id, action->uuid,
1011 (action->rsc? action->rsc->id : "<none>"),
1012 (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1013 (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1014 (node_uuid? ")" : ""));
1015 break;
1016 }
1017
1018 if (details) {
1019 GList *iter = NULL;
1020
1021 crm_trace("\t\t====== Preceding Actions");
1022 for (iter = action->actions_before; iter != NULL; iter = iter->next) {
1023 pe_action_wrapper_t *other = (pe_action_wrapper_t *) iter->data;
1024
1025 pcmk__log_action("\t\t", other->action, false);
1026 }
1027 crm_trace("\t\t====== Subsequent Actions");
1028 for (iter = action->actions_after; iter != NULL; iter = iter->next) {
1029 pe_action_wrapper_t *other = (pe_action_wrapper_t *) iter->data;
1030
1031 pcmk__log_action("\t\t", other->action, false);
1032 }
1033 crm_trace("\t\t====== End");
1034
1035 } else {
1036 crm_trace("\t\t(before=%d, after=%d)",
1037 g_list_length(action->actions_before),
1038 g_list_length(action->actions_after));
1039 }
1040}
1041
1052{
1053 char *shutdown_id = NULL;
1054 pe_action_t *shutdown_op = NULL;
1055
1056 CRM_ASSERT(node != NULL);
1057
1058 shutdown_id = crm_strdup_printf("%s-%s", CRM_OP_SHUTDOWN,
1059 node->details->uname);
1060
1061 shutdown_op = custom_action(NULL, shutdown_id, CRM_OP_SHUTDOWN, node, FALSE,
1062 TRUE, node->details->data_set);
1063
1064 pcmk__order_stops_before_shutdown(node, shutdown_op);
1066 return shutdown_op;
1067}
1068
1080static void
1081add_op_digest_to_xml(lrmd_event_data_t *op, xmlNode *update)
1082{
1083 char *digest = NULL;
1084 xmlNode *args_xml = NULL;
1085
1086 if (op->params == NULL) {
1087 return;
1088 }
1089 args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
1090 g_hash_table_foreach(op->params, hash2field, args_xml);
1092 digest = calculate_operation_digest(args_xml, NULL);
1093 crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
1094 free_xml(args_xml);
1095 free(digest);
1096}
1097
1098#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1099
1113xmlNode *
1115 const char *caller_version, int target_rc,
1116 const char *node, const char *origin)
1117{
1118 char *key = NULL;
1119 char *magic = NULL;
1120 char *op_id = NULL;
1121 char *op_id_additional = NULL;
1122 char *local_user_data = NULL;
1123 const char *exit_reason = NULL;
1124
1125 xmlNode *xml_op = NULL;
1126 const char *task = NULL;
1127
1128 CRM_CHECK(op != NULL, return NULL);
1129 crm_trace("Creating history XML for %s-interval %s action for %s on %s "
1130 "(DC version: %s, origin: %s)",
1132 ((node == NULL)? "no node" : node), caller_version, origin);
1133
1134 task = op->op_type;
1135
1136 /* Record a successful agent reload as a start, and a failed one as a
1137 * monitor, to make life easier for the scheduler when determining the
1138 * current state.
1139 *
1140 * @COMPAT We should check "reload" here only if the operation was for a
1141 * pre-OCF-1.1 resource agent, but we don't know that here, and we should
1142 * only ever get results for actions scheduled by us, so we can reasonably
1143 * assume any "reload" is actually a pre-1.1 agent reload.
1144 */
1146 NULL)) {
1147 if (op->op_status == PCMK_EXEC_DONE) {
1148 task = CRMD_ACTION_START;
1149 } else {
1150 task = CRMD_ACTION_STATUS;
1151 }
1152 }
1153
1154 key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
1155 if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_none)) {
1156 const char *n_type = crm_meta_value(op->params, "notify_type");
1157 const char *n_task = crm_meta_value(op->params, "notify_operation");
1158
1159 CRM_LOG_ASSERT(n_type != NULL);
1160 CRM_LOG_ASSERT(n_task != NULL);
1161 op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
1162
1163 if (op->op_status != PCMK_EXEC_PENDING) {
1164 /* Ignore notify errors.
1165 *
1166 * @TODO It might be better to keep the correct result here, and
1167 * ignore it in process_graph_event().
1168 */
1170 }
1171
1172 /* Migration history is preserved separately, which usually matters for
1173 * multiple nodes and is important for future cluster transitions.
1174 */
1176 CRMD_ACTION_MIGRATED, NULL)) {
1177 op_id = strdup(key);
1178
1179 } else if (did_rsc_op_fail(op, target_rc)) {
1180 op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
1181 if (op->interval_ms == 0) {
1182 // Ensure 'last' gets updated, in case record-pending is true
1183 op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
1184 }
1185 exit_reason = op->exit_reason;
1186
1187 } else if (op->interval_ms > 0) {
1188 op_id = strdup(key);
1189
1190 } else {
1191 op_id = pcmk__op_key(op->rsc_id, "last", 0);
1192 }
1193
1194 again:
1196 if (xml_op == NULL) {
1198 }
1199
1200 if (op->user_data == NULL) {
1201 crm_debug("Generating fake transition key for: " PCMK__OP_FMT
1202 " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
1203 op->call_id, origin);
1204 local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
1205 FAKE_TE_ID);
1206 op->user_data = local_user_data;
1207 }
1208
1209 if (magic == NULL) {
1210 magic = crm_strdup_printf("%d:%d;%s", op->op_status, op->rc,
1211 (const char *) op->user_data);
1212 }
1213
1214 crm_xml_add(xml_op, XML_ATTR_ID, op_id);
1215 crm_xml_add(xml_op, XML_LRM_ATTR_TASK_KEY, key);
1216 crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
1217 crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
1218 crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
1220 crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
1221 crm_xml_add(xml_op, XML_LRM_ATTR_EXIT_REASON, exit_reason == NULL ? "" : exit_reason);
1222 crm_xml_add(xml_op, XML_LRM_ATTR_TARGET, node); /* For context during triage */
1223
1225 crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
1228
1229 if (compare_version("2.1", caller_version) <= 0) {
1230 if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
1231 crm_trace("Timing data (" PCMK__OP_FMT
1232 "): last=%u change=%u exec=%u queue=%u",
1233 op->rsc_id, op->op_type, op->interval_ms,
1234 op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
1235
1236 if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
1237 // Recurring ops may have changed rc after initial run
1239 (long long) op->t_rcchange);
1240 } else {
1242 (long long) op->t_run);
1243 }
1244
1247 }
1248 }
1249
1251 /*
1252 * Record migrate_source and migrate_target always for migrate ops.
1253 */
1254 const char *name = XML_LRM_ATTR_MIGRATE_SOURCE;
1255
1256 crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1257
1259 crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1260 }
1261
1262 add_op_digest_to_xml(op, xml_op);
1263
1264 if (op_id_additional) {
1265 free(op_id);
1266 op_id = op_id_additional;
1267 op_id_additional = NULL;
1268 goto again;
1269 }
1270
1271 if (local_user_data) {
1272 free(local_user_data);
1273 op->user_data = NULL;
1274 }
1275 free(magic);
1276 free(op_id);
1277 free(key);
1278 return xml_op;
1279}
1280
1295bool
1297{
1298 // Only resource actions taking place on resource's lock node are locked
1299 if ((action == NULL) || (action->rsc == NULL)
1300 || (action->rsc->lock_node == NULL) || (action->node == NULL)
1301 || (action->node->details != action->rsc->lock_node->details)) {
1302 return false;
1303 }
1304
1305 /* During shutdown, only stops are locked (otherwise, another action such as
1306 * a demote would cause the controller to clear the lock)
1307 */
1308 if (action->node->details->shutdown && (action->task != NULL)
1309 && (strcmp(action->task, RSC_STOP) != 0)) {
1310 return false;
1311 }
1312
1313 return true;
1314}
1315
1316/* lowest to highest */
1317static gint
1318sort_action_id(gconstpointer a, gconstpointer b)
1319{
1320 const pe_action_wrapper_t *action_wrapper2 = (const pe_action_wrapper_t *)a;
1321 const pe_action_wrapper_t *action_wrapper1 = (const pe_action_wrapper_t *)b;
1322
1323 if (a == NULL) {
1324 return 1;
1325 }
1326 if (b == NULL) {
1327 return -1;
1328 }
1329 if (action_wrapper1->action->id < action_wrapper2->action->id) {
1330 return 1;
1331 }
1332 if (action_wrapper1->action->id > action_wrapper2->action->id) {
1333 return -1;
1334 }
1335 return 0;
1336}
1337
1344void
1346{
1347 GList *item = NULL;
1348 GList *next = NULL;
1349 pe_action_wrapper_t *last_input = NULL;
1350
1351 action->actions_before = g_list_sort(action->actions_before,
1352 sort_action_id);
1353 for (item = action->actions_before; item != NULL; item = next) {
1355
1356 next = item->next;
1357 if ((last_input != NULL)
1358 && (input->action->id == last_input->action->id)) {
1359 crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1360 input->action->uuid, input->action->id,
1361 action->uuid, action->id);
1362
1363 /* For the purposes of scheduling, the ordering flags no longer
1364 * matter, but crm_simulate looks at certain ones when creating a
1365 * dot graph. Combining the flags is sufficient for that purpose.
1366 */
1367 last_input->type |= input->type;
1368 if (input->state == pe_link_dumped) {
1369 last_input->state = pe_link_dumped;
1370 }
1371
1372 free(item->data);
1373 action->actions_before = g_list_delete_link(action->actions_before,
1374 item);
1375 } else {
1376 last_input = input;
1377 input->state = pe_link_not_dumped;
1378 }
1379 }
1380}
1381
1388void
1390{
1392
1393 // Output node (non-resource) actions
1394 for (GList *iter = data_set->actions; iter != NULL; iter = iter->next) {
1395 char *node_name = NULL;
1396 char *task = NULL;
1397 pe_action_t *action = (pe_action_t *) iter->data;
1398
1399 if (action->rsc != NULL) {
1400 continue; // Resource actions will be output later
1401
1402 } else if (pcmk_is_set(action->flags, pe_action_optional)) {
1403 continue; // This action was not scheduled
1404 }
1405
1406 if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
1407 task = strdup("Shutdown");
1408
1409 } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
1410 const char *op = g_hash_table_lookup(action->meta, "stonith_action");
1411
1412 task = crm_strdup_printf("Fence (%s)", op);
1413
1414 } else {
1415 continue; // Don't display other node action types
1416 }
1417
1418 if (pe__is_guest_node(action->node)) {
1419 node_name = crm_strdup_printf("%s (resource: %s)",
1420 pe__node_name(action->node),
1421 action->node->details->remote_rsc->container->id);
1422 } else if (action->node != NULL) {
1423 node_name = crm_strdup_printf("%s", pe__node_name(action->node));
1424 }
1425
1426 out->message(out, "node-action", task, node_name, action->reason);
1427
1428 free(node_name);
1429 free(task);
1430 }
1431
1432 // Output resource actions
1433 for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
1434 pe_resource_t *rsc = (pe_resource_t *) iter->data;
1435
1436 rsc->cmds->output_actions(rsc);
1437 }
1438}
1439
1450static bool
1451action_in_config(pe_resource_t *rsc, const char *task, guint interval_ms)
1452{
1453 char *key = pcmk__op_key(rsc->id, task, interval_ms);
1454 bool config = (find_rsc_op_entry(rsc, key) != NULL);
1455
1456 free(key);
1457 return config;
1458}
1459
1469static const char *
1470task_for_digest(const char *task, guint interval_ms)
1471{
1472 /* Certain actions need to be compared against the parameters used to start
1473 * the resource.
1474 */
1475 if ((interval_ms == 0)
1477 task = RSC_START;
1478 }
1479 return task;
1480}
1481
1499static bool
1500only_sanitized_changed(xmlNode *xml_op, const op_digest_cache_t *digest_data,
1502{
1503 const char *digest_secure = NULL;
1504
1506 // The scheduler is not being run as a simulation
1507 return false;
1508 }
1509
1510 digest_secure = crm_element_value(xml_op, XML_LRM_ATTR_SECURE_DIGEST);
1511
1512 return (digest_data->rc != RSC_DIGEST_MATCH) && (digest_secure != NULL)
1513 && (digest_data->digest_secure_calc != NULL)
1514 && (strcmp(digest_data->digest_secure_calc, digest_secure) == 0);
1515}
1516
1526static void
1527force_restart(pe_resource_t *rsc, const char *task, guint interval_ms,
1528 pe_node_t *node)
1529{
1530 char *key = pcmk__op_key(rsc->id, task, interval_ms);
1531 pe_action_t *required = custom_action(rsc, key, task, NULL, FALSE, TRUE,
1532 rsc->cluster);
1533
1534 pe_action_set_reason(required, "resource definition change", true);
1535 trigger_unfencing(rsc, node, "Device parameters changed", NULL,
1536 rsc->cluster);
1537}
1538
1546static void
1547schedule_reload(pe_resource_t *rsc, pe_node_t *node)
1548{
1549 pe_action_t *reload = NULL;
1550
1551 // For collective resources, just call recursively for children
1552 if (rsc->variant > pe_native) {
1553 g_list_foreach(rsc->children, (GFunc) schedule_reload, node);
1554 return;
1555 }
1556
1557 // Skip the reload in certain situations
1558 if ((node == NULL)
1560 || pcmk_is_set(rsc->flags, pe_rsc_failed)) {
1561 pe_rsc_trace(rsc, "Skip reload of %s:%s%s %s",
1562 rsc->id,
1563 pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " unmanaged",
1564 pcmk_is_set(rsc->flags, pe_rsc_failed)? " failed" : "",
1565 (node == NULL)? "inactive" : node->details->uname);
1566 return;
1567 }
1568
1569 /* If a resource's configuration changed while a start was pending,
1570 * force a full restart instead of a reload.
1571 */
1573 pe_rsc_trace(rsc, "%s: preventing agent reload because start pending",
1574 rsc->id);
1575 custom_action(rsc, stop_key(rsc), CRMD_ACTION_STOP, node, FALSE, TRUE,
1576 rsc->cluster);
1577 return;
1578 }
1579
1580 // Schedule the reload
1582 reload = custom_action(rsc, reload_key(rsc), CRMD_ACTION_RELOAD_AGENT, node,
1583 FALSE, TRUE, rsc->cluster);
1584 pe_action_set_reason(reload, "resource definition change", FALSE);
1585
1586 // Set orderings so that a required stop or demote cancels the reload
1587 pcmk__new_ordering(NULL, NULL, reload, rsc, stop_key(rsc), NULL,
1589 rsc->cluster);
1590 pcmk__new_ordering(NULL, NULL, reload, rsc, demote_key(rsc), NULL,
1592 rsc->cluster);
1593}
1594
1609bool
1611{
1612 guint interval_ms = 0;
1613 const char *task = NULL;
1614 const op_digest_cache_t *digest_data = NULL;
1615
1616 CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL),
1617 return false);
1618
1619 task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
1620 CRM_CHECK(task != NULL, return false);
1621
1622 crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
1623
1624 // If this is a recurring action, check whether it has been orphaned
1625 if (interval_ms > 0) {
1626 if (action_in_config(rsc, task, interval_ms)) {
1627 pe_rsc_trace(rsc, "%s-interval %s for %s on %s is in configuration",
1628 pcmk__readable_interval(interval_ms), task, rsc->id,
1629 pe__node_name(node));
1630 } else if (pcmk_is_set(rsc->cluster->flags,
1634 task, interval_ms, node, "orphan");
1635 return true;
1636 } else {
1637 pe_rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned",
1638 pcmk__readable_interval(interval_ms), task, rsc->id,
1639 pe__node_name(node));
1640 return true;
1641 }
1642 }
1643
1644 crm_trace("Checking %s-interval %s for %s on %s for configuration changes",
1645 pcmk__readable_interval(interval_ms), task, rsc->id,
1646 pe__node_name(node));
1647 task = task_for_digest(task, interval_ms);
1648 digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster);
1649
1650 if (only_sanitized_changed(xml_op, digest_data, rsc->cluster)) {
1651 if (!pcmk__is_daemon && (rsc->cluster->priv != NULL)) {
1652 pcmk__output_t *out = rsc->cluster->priv;
1653
1654 out->info(out,
1655 "Only 'private' parameters to %s-interval %s for %s "
1656 "on %s changed: %s",
1657 pcmk__readable_interval(interval_ms), task, rsc->id,
1658 pe__node_name(node),
1660 }
1661 return false;
1662 }
1663
1664 switch (digest_data->rc) {
1665 case RSC_DIGEST_RESTART:
1666 crm_log_xml_debug(digest_data->params_restart, "params:restart");
1667 force_restart(rsc, task, interval_ms, node);
1668 return true;
1669
1670 case RSC_DIGEST_ALL:
1671 case RSC_DIGEST_UNKNOWN:
1672 // Changes that can potentially be handled by an agent reload
1673
1674 if (interval_ms > 0) {
1675 /* Recurring actions aren't reloaded per se, they are just
1676 * re-scheduled so the next run uses the new parameters.
1677 * The old instance will be cancelled automatically.
1678 */
1679 crm_log_xml_debug(digest_data->params_all, "params:reschedule");
1680 pcmk__reschedule_recurring(rsc, task, interval_ms, node);
1681
1682 } else if (crm_element_value(xml_op,
1683 XML_LRM_ATTR_RESTART_DIGEST) != NULL) {
1684 // Agent supports reload, so use it
1685 trigger_unfencing(rsc, node,
1686 "Device parameters changed (reload)", NULL,
1687 rsc->cluster);
1688 crm_log_xml_debug(digest_data->params_all, "params:reload");
1689 schedule_reload(rsc, node);
1690
1691 } else {
1692 pe_rsc_trace(rsc,
1693 "Restarting %s because agent doesn't support reload",
1694 rsc->id);
1695 crm_log_xml_debug(digest_data->params_restart,
1696 "params:restart");
1697 force_restart(rsc, task, interval_ms, node);
1698 }
1699 return true;
1700
1701 default:
1702 break;
1703 }
1704 return false;
1705}
1706
1716static GList *
1717rsc_history_as_list(pe_resource_t *rsc, xmlNode *rsc_entry,
1718 int *start_index, int *stop_index)
1719{
1720 GList *ops = NULL;
1721
1722 for (xmlNode *rsc_op = first_named_child(rsc_entry, XML_LRM_TAG_RSC_OP);
1723 rsc_op != NULL; rsc_op = crm_next_same_xml(rsc_op)) {
1724 ops = g_list_prepend(ops, rsc_op);
1725 }
1726 ops = g_list_sort(ops, sort_op_by_callid);
1727 calculate_active_ops(ops, start_index, stop_index);
1728 return ops;
1729}
1730
1745static void
1746process_rsc_history(xmlNode *rsc_entry, pe_resource_t *rsc, pe_node_t *node)
1747{
1748 int offset = -1;
1749 int stop_index = 0;
1750 int start_index = 0;
1751 GList *sorted_op_list = NULL;
1752
1753 if (pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
1754 if (pe_rsc_is_anon_clone(uber_parent(rsc))) {
1755 pe_rsc_trace(rsc,
1756 "Skipping configuration check "
1757 "for orphaned clone instance %s",
1758 rsc->id);
1759 } else {
1760 pe_rsc_trace(rsc,
1761 "Skipping configuration check and scheduling clean-up "
1762 "for orphaned resource %s", rsc->id);
1763 pcmk__schedule_cleanup(rsc, node, false);
1764 }
1765 return;
1766 }
1767
1768 if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
1769 if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) {
1770 pcmk__schedule_cleanup(rsc, node, false);
1771 }
1772 pe_rsc_trace(rsc,
1773 "Skipping configuration check for %s "
1774 "because no longer active on %s",
1775 rsc->id, pe__node_name(node));
1776 return;
1777 }
1778
1779 pe_rsc_trace(rsc, "Checking for configuration changes for %s on %s",
1780 rsc->id, pe__node_name(node));
1781
1782 if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) {
1783 pcmk__schedule_cleanup(rsc, node, false);
1784 }
1785
1786 sorted_op_list = rsc_history_as_list(rsc, rsc_entry, &start_index,
1787 &stop_index);
1788 if (start_index < stop_index) {
1789 return; // Resource is stopped
1790 }
1791
1792 for (GList *iter = sorted_op_list; iter != NULL; iter = iter->next) {
1793 xmlNode *rsc_op = (xmlNode *) iter->data;
1794 const char *task = NULL;
1795 guint interval_ms = 0;
1796
1797 if (++offset < start_index) {
1798 // Skip actions that happened before a start
1799 continue;
1800 }
1801
1802 task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
1803 crm_element_value_ms(rsc_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
1804
1805 if ((interval_ms > 0)
1807 || node->details->maintenance)) {
1808 // Maintenance mode cancels recurring operations
1811 task, interval_ms, node, "maintenance mode");
1812
1813 } else if ((interval_ms > 0)
1815 RSC_PROMOTE, RSC_MIGRATED, NULL)) {
1816 /* If a resource operation failed, and the operation's definition
1817 * has changed, clear any fail count so they can be retried fresh.
1818 */
1819
1820 if (pe__bundle_needs_remote_name(rsc, rsc->cluster)) {
1821 /* We haven't allocated resources to nodes yet, so if the
1822 * REMOTE_CONTAINER_HACK is used, we may calculate the digest
1823 * based on the literal "#uname" value rather than the properly
1824 * substituted value. That would mistakenly make the action
1825 * definition appear to have been changed. Defer the check until
1826 * later in this case.
1827 */
1828 pe__add_param_check(rsc_op, rsc, node, pe_check_active,
1829 rsc->cluster);
1830
1831 } else if (pcmk__check_action_config(rsc, node, rsc_op)
1832 && (pe_get_failcount(node, rsc, NULL, pe_fc_effective,
1833 NULL, rsc->cluster) != 0)) {
1834 pe__clear_failcount(rsc, node, "action definition changed",
1835 rsc->cluster);
1836 }
1837 }
1838 }
1839 g_list_free(sorted_op_list);
1840}
1841
1856static void
1857process_node_history(pe_node_t *node, xmlNode *lrm_rscs, pe_working_set_t *data_set)
1858{
1859 crm_trace("Processing node history for %s", pe__node_name(node));
1860 for (xmlNode *rsc_entry = first_named_child(lrm_rscs, XML_LRM_TAG_RESOURCE);
1861 rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) {
1862
1863 if (xml_has_children(rsc_entry)) {
1864 GList *result = pcmk__rscs_matching_id(ID(rsc_entry), data_set);
1865
1866 for (GList *iter = result; iter != NULL; iter = iter->next) {
1867 pe_resource_t *rsc = (pe_resource_t *) iter->data;
1868
1869 if (rsc->variant == pe_native) {
1870 process_rsc_history(rsc_entry, rsc, node);
1871 }
1872 }
1873 g_list_free(result);
1874 }
1875 }
1876}
1877
1878// XPath to find a node's resource history
1879#define XPATH_NODE_HISTORY "/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS \
1880 "/" XML_CIB_TAG_STATE "[@" XML_ATTR_UNAME "='%s']" \
1881 "/" XML_CIB_TAG_LRM "/" XML_LRM_TAG_RESOURCES
1882
1895void
1897{
1898 crm_trace("Check resource and action configuration for changes");
1899
1900 /* Rather than iterate through the status section, iterate through the nodes
1901 * and search for the appropriate status subsection for each. This skips
1902 * orphaned nodes and lets us eliminate some cases before searching the XML.
1903 */
1904 for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
1905 pe_node_t *node = (pe_node_t *) iter->data;
1906
1907 /* Don't bother checking actions for a node that can't run actions ...
1908 * unless it's in maintenance mode, in which case we still need to
1909 * cancel any existing recurring monitors.
1910 */
1911 if (node->details->maintenance
1912 || pcmk__node_available(node, false, false)) {
1913
1914 char *xpath = NULL;
1915 xmlNode *history = NULL;
1916
1918 history = get_xpath_object(xpath, data_set->input, LOG_NEVER);
1919 free(xpath);
1920
1921 process_node_history(node, history, data_set);
1922 }
1923 }
1924}
const char * parent
Definition: cib.c:25
const char * name
Definition: cib.c:24
void pcmk__filter_op_for_digest(xmlNode *param_set)
Definition: operations.c:390
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:229
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
char * pcmk__transition_key(int transition_id, int action_id, int target_rc, const char *node)
Definition: operations.c:296
bool pcmk__is_daemon
Definition: logging.c:47
#define PCMK__OP_FMT
Definition: internal.h:168
uint64_t flags
Definition: remote.c:3
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:185
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:490
gboolean did_rsc_op_fail(lrmd_event_data_t *event, int target_rc)
Definition: operations.c:437
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
enum action_tasks text2task(const char *task)
Definition: common.c:353
void calculate_active_ops(GList *sorted_op_list, int *start_index, int *stop_index)
Definition: unpack.c:2290
const char * task2text(enum action_tasks task)
Definition: common.c:401
action_tasks
Definition: common.h:61
@ no_action
Definition: common.h:62
@ started_rsc
Definition: common.h:67
@ shutdown_crm
Definition: common.h:74
@ start_rsc
Definition: common.h:66
@ action_demote
Definition: common.h:72
@ stonith_node
Definition: common.h:75
@ action_demoted
Definition: common.h:73
@ action_notified
Definition: common.h:69
@ stop_rsc
Definition: common.h:64
@ action_promote
Definition: common.h:70
@ monitor_rsc
Definition: common.h:63
@ action_promoted
Definition: common.h:71
@ stopped_rsc
Definition: common.h:65
@ action_notify
Definition: common.h:68
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
@ RSC_ROLE_STARTED
Definition: common.h:95
@ RSC_ROLE_STOPPED
Definition: common.h:94
@ RSC_ROLE_PROMOTED
Definition: common.h:97
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:912
gboolean is_parent(pe_resource_t *child, pe_resource_t *rsc)
Definition: complex.c:895
enum crm_ais_msg_types type
Definition: cpg.c:3
#define CRMD_ACTION_STOP
Definition: crm.h:177
#define RSC_PROMOTE
Definition: crm.h:205
#define CRMD_ACTION_NOTIFY
Definition: crm.h:185
#define CRM_OP_SHUTDOWN
Definition: crm.h:143
#define CRMD_ACTION_RELOAD_AGENT
Definition: crm.h:170
#define CRMD_ACTION_MIGRATED
Definition: crm.h:172
#define CRMD_ACTION_STATUS
Definition: crm.h:188
#define RSC_START
Definition: crm.h:199
#define CRMD_ACTION_RELOAD
Definition: crm.h:169
#define RSC_STOP
Definition: crm.h:202
#define CRMD_ACTION_MIGRATE
Definition: crm.h:171
#define CRMD_ACTION_START
Definition: crm.h:174
#define RSC_STATUS
Definition: crm.h:213
#define RSC_MIGRATED
Definition: crm.h:197
#define CRM_OP_FENCE
Definition: crm.h:144
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:1765
G_GNUC_INTERNAL void pcmk__schedule_cleanup(pe_resource_t *rsc, const pe_node_t *node, bool optional)
G_GNUC_INTERNAL void pcmk__order_stops_before_shutdown(pe_node_t *node, pe_action_t *shutdown_op)
G_GNUC_INTERNAL void pcmk__reschedule_recurring(pe_resource_t *rsc, const char *task, guint interval_ms, pe_node_t *node)
#define pcmk__set_updated_flags(au_flags, action, flags_to_set)
G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pe_action_t *action, pe_working_set_t *data_set)
#define pcmk__clear_updated_flags(au_flags, action, flags_to_clear)
@ pcmk__updated_none
@ pcmk__updated_first
@ pcmk__updated_then
G_GNUC_INTERNAL GList * pcmk__rscs_matching_id(const char *id, pe_working_set_t *data_set)
G_GNUC_INTERNAL void pcmk__schedule_cancel(pe_resource_t *rsc, const char *call_id, const char *task, guint interval_ms, const pe_node_t *node, const char *reason)
G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest)
G_GNUC_INTERNAL void pcmk__new_ordering(pe_resource_t *first_rsc, char *first_task, pe_action_t *first_action, pe_resource_t *then_rsc, char *then_task, pe_action_t *then_action, uint32_t flags, pe_working_set_t *data_set)
G_GNUC_INTERNAL bool pcmk__rsc_agent_changed(pe_resource_t *rsc, pe_node_t *node, const xmlNode *rsc_entry, bool active_on_node)
#define crm_warn(fmt, args...)
Definition: logging.h:360
#define crm_log_xml_debug(xml, text)
Definition: logging.h:372
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define LOG_NEVER
Definition: logging.h:47
#define crm_trace(fmt, args...)
Definition: logging.h:365
void lrmd__set_result(lrmd_event_data_t *event, enum ocf_exitcode rc, int op_status, const char *exit_reason)
Definition: lrmd_client.c:2472
#define XML_LRM_TAG_RSC_OP
Definition: msg_xml.h:268
#define ID(x)
Definition: msg_xml.h:468
#define XML_ATTR_TRANSITION_KEY
Definition: msg_xml.h:413
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:118
#define XML_BOOLEAN_TRUE
Definition: msg_xml.h:146
#define XML_ATTR_TE_NOWAIT
Definition: msg_xml.h:415
#define XML_LRM_ATTR_OP_DIGEST
Definition: msg_xml.h:313
#define XML_LRM_ATTR_SECURE_DIGEST
Definition: msg_xml.h:317
#define XML_LRM_ATTR_MIGRATE_SOURCE
Definition: msg_xml.h:325
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:301
#define XML_LRM_ATTR_OPSTATUS
Definition: msg_xml.h:310
#define XML_ATTR_ID
Definition: msg_xml.h:134
#define XML_ATTR_TRANSITION_MAGIC
Definition: msg_xml.h:412
#define XML_RSC_OP_T_EXEC
Definition: msg_xml.h:322
#define XML_LRM_ATTR_RESTART_DIGEST
Definition: msg_xml.h:316
#define XML_LRM_ATTR_EXIT_REASON
Definition: msg_xml.h:318
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:129
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:300
#define XML_LRM_ATTR_MIGRATE_TARGET
Definition: msg_xml.h:326
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:302
#define XML_RSC_OP_LAST_CHANGE
Definition: msg_xml.h:320
#define XML_LRM_ATTR_CALLID
Definition: msg_xml.h:312
#define XML_LRM_ATTR_RC
Definition: msg_xml.h:311
#define XML_RSC_OP_T_QUEUE
Definition: msg_xml.h:323
#define XML_TAG_PARAMS
Definition: msg_xml.h:212
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:298
#define XML_LRM_TAG_RESOURCE
Definition: msg_xml.h:267
pe_working_set_t * data_set
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:770
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:419
int crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
Retrieve the millisecond value of an XML attribute.
Definition: nvpair.c:610
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.
Definition: nvpair.c:469
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
const char * crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
Create an XML attribute with specified name and unsigned value.
Definition: nvpair.c:441
const char * action
Definition: pcmk_fence.c:30
pcmk__action_result_t result
Definition: pcmk_fence.c:35
void pcmk__log_action(const char *pre_text, pe_action_t *action, bool details)
#define XPATH_NODE_HISTORY
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)
pe_action_t * pcmk__new_shutdown_action(pe_node_t *node)
void pcmk__handle_rsc_config_changes(pe_working_set_t *data_set)
void pcmk__update_action_for_orderings(pe_action_t *then, pe_working_set_t *data_set)
#define action_type_str(flags)
void pcmk__deduplicate_action_inputs(pe_action_t *action)
#define action_node_str(a)
void pcmk__output_actions(pe_working_set_t *data_set)
#define action_runnable_str(flags)
bool pcmk__check_action_config(pe_resource_t *rsc, pe_node_t *node, xmlNode *xml_op)
#define clear_action_flag_because(action, flag, reason)
uint32_t pcmk__update_ordered_actions(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set)
bool pcmk__action_locks_rsc_to_node(const pe_action_t *action)
#define FAKE_TE_ID
#define action_optional_str(flags)
#define pe_rsc_notify
Definition: pe_types.h:261
@ pe_check_active
Definition: pe_types.h:211
#define pe_rsc_block
Definition: pe_types.h:258
#define pe_rsc_managed
Definition: pe_types.h:257
@ pe_order_implies_first_printed
Definition: pe_types.h:508
@ pe_order_pseudo_left
Definition: pe_types.h:493
@ pe_order_restart
Definition: pe_types.h:503
@ pe_order_implies_then
Definition: pe_types.h:485
@ pe_order_same_node
Definition: pe_types.h:506
@ pe_order_one_or_more
Definition: pe_types.h:513
@ pe_order_none
Definition: pe_types.h:480
@ pe_order_then_cancels_first
Definition: pe_types.h:517
@ pe_order_implies_then_on_node
Definition: pe_types.h:494
@ pe_order_asymmetrical
Definition: pe_types.h:511
@ pe_order_implies_first_migratable
Definition: pe_types.h:489
@ pe_order_implies_then_printed
Definition: pe_types.h:509
@ pe_order_optional
Definition: pe_types.h:481
@ pe_order_implies_first
Definition: pe_types.h:484
@ pe_order_runnable_left
Definition: pe_types.h:491
@ pe_order_probe
Definition: pe_types.h:498
@ pe_order_promoted_implies_first
Definition: pe_types.h:486
#define pe_rsc_orphan
Definition: pe_types.h:256
#define pe_rsc_reload
Definition: pe_types.h:272
@ pe_link_not_dumped
Definition: pe_types.h:467
@ pe_link_dumped
Internal tracking for transition graph creation.
Definition: pe_types.h:468
pe_action_flags
Definition: pe_types.h:298
@ pe_action_optional
Definition: pe_types.h:301
@ pe_action_processed
Definition: pe_types.h:309
@ pe_action_runnable
Definition: pe_types.h:300
@ pe_action_pseudo
Definition: pe_types.h:299
@ pe_action_print_always
Definition: pe_types.h:302
@ pe_action_requires_any
Definition: pe_types.h:318
@ pe_action_migrate_runnable
Definition: pe_types.h:306
@ pe_group
Definition: pe_types.h:39
@ pe_container
Definition: pe_types.h:41
@ pe_native
Definition: pe_types.h:38
#define pe_rsc_maintenance
Definition: pe_types.h:290
#define pe_rsc_failed
Definition: pe_types.h:276
#define pe_flag_sanitized
Definition: pe_types.h:121
#define pe_flag_stop_action_orphans
Definition: pe_types.h:105
#define pe_rsc_start_pending
Definition: pe_types.h:278
#define demote_key(rsc)
Definition: internal.h:440
#define reload_key(rsc)
Definition: internal.h:419
op_digest_cache_t * rsc_action_digest_cmp(pe_resource_t *rsc, xmlNode *xml_op, pe_node_t *node, pe_working_set_t *data_set)
Definition: pe_digest.c:380
#define pe__set_raw_action_flags(action_flags, action_name, flags_to_set)
Definition: internal.h:104
void trigger_unfencing(pe_resource_t *rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t *data_set)
Definition: utils.c:604
pe_action_t * custom_action(pe_resource_t *rsc, char *key, const char *task, const pe_node_t *on_node, gboolean optional, gboolean foo, pe_working_set_t *data_set)
Create or update an action object.
Definition: pe_actions.c:940
pe_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pe_node_t *on_node)
Definition: pe_actions.c:1296
int pe_get_failcount(pe_node_t *node, pe_resource_t *rsc, time_t *last_failure, uint32_t flags, xmlNode *xml_op, pe_working_set_t *data_set)
Definition: failcounts.c:251
#define pe__clear_order_flags(order_flags, flags_to_clear)
Definition: internal.h:145
#define stop_key(rsc)
Definition: internal.h:414
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:80
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:46
bool pe__bundle_needs_remote_name(pe_resource_t *rsc, pe_working_set_t *data_set)
Definition: bundle.c:690
void pe__add_param_check(xmlNode *rsc_op, pe_resource_t *rsc, pe_node_t *node, enum pe_check_parameters, pe_working_set_t *data_set)
Definition: remote.c:220
gboolean order_actions(pe_action_t *lh_action, pe_action_t *rh_action, enum pe_ordering order)
Definition: utils.c:474
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
pe_action_t * pe__clear_failcount(pe_resource_t *rsc, pe_node_t *node, const char *reason, pe_working_set_t *data_set)
Schedule a controller operation to clear a fail count.
Definition: failcounts.c:360
@ RSC_DIGEST_ALL
Definition: internal.h:510
@ RSC_DIGEST_UNKNOWN
Definition: internal.h:513
@ RSC_DIGEST_RESTART
Definition: internal.h:508
@ RSC_DIGEST_MATCH
Definition: internal.h:506
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
Definition: pe_actions.c:1627
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:74
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:95
@ pe_fc_effective
Definition: internal.h:345
xmlNode * find_rsc_op_entry(const pe_resource_t *rsc, const char *key)
Definition: pe_actions.c:145
void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1447
#define pe__set_order_flags(order_flags, flags_to_set)
Definition: internal.h:138
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:86
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition: common.c:504
bool pe__is_guest_node(const pe_node_t *node)
Definition: remote.c:33
#define CRM_ASSERT(expr)
Definition: results.h:42
@ PCMK_OCF_OK
Success.
Definition: results.h:164
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition: results.h:312
@ PCMK_EXEC_PENDING
Action is in progress.
Definition: results.h:311
pe_node_t * pe_find_node_id(GList *node_list, const char *id)
Definition: status.c:427
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
const char * op_type
Definition: lrmd.h:223
unsigned int t_run
Definition: lrmd.h:245
unsigned int t_rcchange
Definition: lrmd.h:247
const char * exit_reason
Definition: lrmd.h:266
const char * user_data
Definition: lrmd.h:225
unsigned int exec_time
Definition: lrmd.h:249
enum ocf_exitcode rc
Definition: lrmd.h:239
unsigned int queue_time
Definition: lrmd.h:251
void * params
Definition: lrmd.h:258
guint interval_ms
Definition: lrmd.h:232
const char * rsc_id
Definition: lrmd.h:221
char * digest_secure_calc
Definition: internal.h:522
xmlNode * params_restart
Definition: internal.h:520
enum rsc_digest_cmp_val rc
Definition: internal.h:517
xmlNode * params_all
Definition: internal.h:518
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
int runnable_before
Definition: pe_types.h:438
pe_resource_t * rsc
Definition: pe_types.h:406
char * uuid
Definition: pe_types.h:411
char * task
Definition: pe_types.h:410
pe_node_t * node
Definition: pe_types.h:407
GList * actions_after
Definition: pe_types.h:444
GHashTable * meta
Definition: pe_types.h:420
enum pe_action_flags flags
Definition: pe_types.h:415
int required_runnable_before
Definition: pe_types.h:441
GList * actions_before
Definition: pe_types.h:443
enum pe_ordering type
Definition: pe_types.h:528
enum pe_link_state state
Definition: pe_types.h:529
pe_action_t * action
Definition: pe_types.h:530
struct pe_node_shared_s * details
Definition: pe_types.h:252
const char * id
Definition: pe_types.h:215
const char * uname
Definition: pe_types.h:216
pe_working_set_t * data_set
Cluster that this node is part of.
Definition: pe_types.h:245
gboolean maintenance
Definition: pe_types.h:229
GList * running_on
Definition: pe_types.h:373
GList * actions
Definition: pe_types.h:366
enum pe_obj_types variant
Definition: pe_types.h:338
GList * children
Definition: pe_types.h:384
pe_working_set_t * cluster
Definition: pe_types.h:335
char * id
Definition: pe_types.h:329
unsigned long long flags
Definition: pe_types.h:355
pe_resource_t * parent
Definition: pe_types.h:336
resource_alloc_functions_t * cmds
Definition: pe_types.h:341
enum rsc_role_e role
Definition: pe_types.h:377
resource_object_functions_t * fns
Definition: pe_types.h:340
GList * actions
Definition: pe_types.h:171
xmlNode * input
Definition: pe_types.h:144
GList * resources
Definition: pe_types.h:165
unsigned long long flags
Definition: pe_types.h:153
GList * nodes
Definition: pe_types.h:164
uint32_t(* update_ordered_actions)(pe_action_t *first, pe_action_t *then, const pe_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pe_working_set_t *data_set)
void(* output_actions)(pe_resource_t *rsc)
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:55
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:54
gboolean xml_has_children(const xmlNode *root)
Definition: xml.c:2136
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
xmlNode * get_xpath_object(const char *xpath, xmlNode *xml_obj, int error_level)
Definition: xpath.c:214
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956
void free_xml(xmlNode *child)
Definition: xml.c:885
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:153
xmlNode * pcmk__xe_match(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:520