pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
pcmk_graph_producer.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 <sys/param.h>
13 #include <crm/crm.h>
14 #include <crm/cib.h>
15 #include <crm/msg_xml.h>
16 #include <crm/common/xml.h>
17 
18 #include <glib.h>
19 
20 #include <pacemaker-internal.h>
21 
22 #include "libpacemaker_private.h"
23 
24 gboolean rsc_update_action(pe_action_t * first, pe_action_t * then, enum pe_ordering type);
25 
26 static enum pe_action_flags
27 get_action_flags(pe_action_t * action, pe_node_t * node)
28 {
29  enum pe_action_flags flags = action->flags;
30 
31  if (action->rsc) {
32  flags = action->rsc->cmds->action_flags(action, NULL);
33 
34  if (pe_rsc_is_clone(action->rsc) && node) {
35 
36  /* We only care about activity on $node */
37  enum pe_action_flags clone_flags = action->rsc->cmds->action_flags(action, node);
38 
39  /* Go to great lengths to ensure the correct value for pe_action_runnable...
40  *
41  * If we are a clone, then for _ordering_ constraints, it's only relevant
42  * if we are runnable _anywhere_.
43  *
44  * This only applies to _runnable_ though, and only for ordering constraints.
45  * If this function is ever used during colocation, then we'll need additional logic
46  *
47  * Not very satisfying, but it's logical and appears to work well.
48  */
49  if (!pcmk_is_set(clone_flags, pe_action_runnable)
51 
52  pe__set_raw_action_flags(clone_flags, action->rsc->id,
54  }
55  flags = clone_flags;
56  }
57  }
58  return flags;
59 }
60 
61 static char *
62 convert_non_atomic_uuid(char *old_uuid, pe_resource_t * rsc, gboolean allow_notify,
63  gboolean free_original)
64 {
65  guint interval_ms = 0;
66  char *uuid = NULL;
67  char *rid = NULL;
68  char *raw_task = NULL;
69  int task = no_action;
70 
71  CRM_ASSERT(rsc);
72  pe_rsc_trace(rsc, "Processing %s", old_uuid);
73  if (old_uuid == NULL) {
74  return NULL;
75 
76  } else if (strstr(old_uuid, "notify") != NULL) {
77  goto done; /* no conversion */
78 
79  } else if (rsc->variant < pe_group) {
80  goto done; /* no conversion */
81  }
82 
83  CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval_ms));
84  if (interval_ms > 0) {
85  goto done; /* no conversion */
86  }
87 
88  task = text2task(raw_task);
89  switch (task) {
90  case stop_rsc:
91  case start_rsc:
92  case action_notify:
93  case action_promote:
94  case action_demote:
95  break;
96  case stopped_rsc:
97  case started_rsc:
98  case action_notified:
99  case action_promoted:
100  case action_demoted:
101  task--;
102  break;
103  case monitor_rsc:
104  case shutdown_crm:
105  case stonith_node:
106  task = no_action;
107  break;
108  default:
109  crm_err("Unknown action: %s", raw_task);
110  task = no_action;
111  break;
112  }
113 
114  if (task != no_action) {
115  if (pcmk_is_set(rsc->flags, pe_rsc_notify) && allow_notify) {
116  uuid = pcmk__notify_key(rid, "confirmed-post", task2text(task + 1));
117 
118  } else {
119  uuid = pcmk__op_key(rid, task2text(task + 1), 0);
120  }
121  pe_rsc_trace(rsc, "Converted %s -> %s", old_uuid, uuid);
122  }
123 
124  done:
125  if (uuid == NULL) {
126  uuid = strdup(old_uuid);
127  }
128 
129  if (free_original) {
130  free(old_uuid);
131  }
132 
133  free(raw_task);
134  free(rid);
135  return uuid;
136 }
137 
138 static pe_action_t *
139 rsc_expand_action(pe_action_t * action)
140 {
141  gboolean notify = FALSE;
142  pe_action_t *result = action;
143  pe_resource_t *rsc = action->rsc;
144 
145  if (rsc == NULL) {
146  return action;
147  }
148 
149  if ((rsc->parent == NULL)
150  || (pe_rsc_is_clone(rsc) && (rsc->parent->variant == pe_container))) {
151  /* Only outermost resources have notification actions.
152  * The exception is those in bundles.
153  */
154  notify = pcmk_is_set(rsc->flags, pe_rsc_notify);
155  }
156 
157  if (rsc->variant >= pe_group) {
158  /* Expand 'start' -> 'started' */
159  char *uuid = NULL;
160 
161  uuid = convert_non_atomic_uuid(action->uuid, rsc, notify, FALSE);
162  if (uuid) {
163  pe_rsc_trace(rsc, "Converting %s to %s %d", action->uuid, uuid,
165  result = find_first_action(rsc->actions, uuid, NULL, NULL);
166  if (result == NULL) {
167  crm_err("Couldn't expand %s to %s in %s", action->uuid, uuid, rsc->id);
168  result = action;
169  }
170  free(uuid);
171  }
172  }
173  return result;
174 }
175 
176 static enum pe_graph_flags
177 graph_update_action(pe_action_t * first, pe_action_t * then, pe_node_t * node,
178  enum pe_action_flags first_flags, enum pe_action_flags then_flags,
179  pe_action_wrapper_t *order, pe_working_set_t *data_set)
180 {
181  enum pe_graph_flags changed = pe_graph_none;
182  enum pe_ordering type = order->type;
183 
184  /* TODO: Do as many of these in parallel as possible */
185 
187  /* Normally we want the _whole_ 'then' clone to
188  * restart if 'first' is restarted, so then->node is
189  * needed.
190  *
191  * However for unfencing, we want to limit this to
192  * instances on the same node as 'first' (the
193  * unfencing operation), so first->node is supplied.
194  *
195  * Swap the node, from then on we can can treat it
196  * like any other 'pe_order_implies_then'
197  */
198 
201  node = first->node;
202  pe_rsc_trace(then->rsc,
203  "%s then %s: mapped pe_order_implies_then_on_node to "
204  "pe_order_implies_then on %s",
205  first->uuid, then->uuid, node->details->uname);
206  }
207 
208  if (type & pe_order_implies_then) {
209  if (then->rsc) {
210  changed |= then->rsc->cmds->update_actions(first, then, node,
211  first_flags & pe_action_optional, pe_action_optional,
212  pe_order_implies_then, data_set);
213 
214  } else if (!pcmk_is_set(first_flags, pe_action_optional)
215  && pcmk_is_set(then->flags, pe_action_optional)) {
218  }
219  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_then",
220  first->uuid, then->uuid,
221  (changed? "changed" : "unchanged"));
222  }
223 
224  if ((type & pe_order_restart) && then->rsc) {
226 
227  changed |= then->rsc->cmds->update_actions(first, then, node,
228  first_flags, restart,
229  pe_order_restart, data_set);
230  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_restart",
231  first->uuid, then->uuid,
232  (changed? "changed" : "unchanged"));
233  }
234 
236  if (first->rsc) {
237  changed |= first->rsc->cmds->update_actions(first, then, node,
239  data_set);
240 
241  } else if (!pcmk_is_set(first_flags, pe_action_optional)
242  && pcmk_is_set(first->flags, pe_action_runnable)) {
245  }
246  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_implies_first",
247  first->uuid, then->uuid,
248  (changed? "changed" : "unchanged"));
249  }
250 
252  if (then->rsc) {
253  changed |= then->rsc->cmds->update_actions(first, then, node,
254  first_flags & pe_action_optional, pe_action_optional,
256  }
257  pe_rsc_trace(then->rsc,
258  "%s then %s: %s after pe_order_promoted_implies_first",
259  first->uuid, then->uuid,
260  (changed? "changed" : "unchanged"));
261  }
262 
263  if (type & pe_order_one_or_more) {
264  if (then->rsc) {
265  changed |= then->rsc->cmds->update_actions(first, then, node,
267  data_set);
268 
269  } else if (pcmk_is_set(first_flags, pe_action_runnable)) {
270  // We have another runnable instance of "first"
271  then->runnable_before++;
272 
273  /* Mark "then" as runnable if it requires a certain number of
274  * "before" instances to be runnable, and they now are.
275  */
276  if ((then->runnable_before >= then->required_runnable_before)
277  && !pcmk_is_set(then->flags, pe_action_runnable)) {
278 
281  }
282  }
283  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_one_or_more",
284  first->uuid, then->uuid,
285  (changed? "changed" : "unchanged"));
286  }
287 
288  if (then->rsc && pcmk_is_set(type, pe_order_probe)) {
289  if (!pcmk_is_set(first_flags, pe_action_runnable)
290  && (first->rsc->running_on != NULL)) {
291 
292  pe_rsc_trace(then->rsc,
293  "%s then %s: ignoring because first is stopping",
294  first->uuid, then->uuid);
296  order->type = pe_order_none;
297 
298  } else {
299  changed |= then->rsc->cmds->update_actions(first, then, node,
301  data_set);
302  }
303  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_probe",
304  first->uuid, then->uuid,
305  (changed? "changed" : "unchanged"));
306  }
307 
309  if (then->rsc) {
310  changed |= then->rsc->cmds->update_actions(first, then, node,
312  data_set);
313 
314  } else if (!pcmk_is_set(first_flags, pe_action_runnable)
315  && pcmk_is_set(then->flags, pe_action_runnable)) {
316 
319  }
320  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_runnable_left",
321  first->uuid, then->uuid,
322  (changed? "changed" : "unchanged"));
323  }
324 
326  if (then->rsc) {
327  changed |= then->rsc->cmds->update_actions(first, then, node,
328  first_flags, pe_action_optional,
330  }
331  pe_rsc_trace(then->rsc, "%s then %s: %s after "
332  "pe_order_implies_first_migratable",
333  first->uuid, then->uuid,
334  (changed? "changed" : "unchanged"));
335  }
336 
337  if (type & pe_order_pseudo_left) {
338  if (then->rsc) {
339  changed |= then->rsc->cmds->update_actions(first, then, node,
341  data_set);
342  }
343  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_pseudo_left",
344  first->uuid, then->uuid,
345  (changed? "changed" : "unchanged"));
346  }
347 
348  if (type & pe_order_optional) {
349  if (then->rsc) {
350  changed |= then->rsc->cmds->update_actions(first, then, node,
351  first_flags, pe_action_runnable, pe_order_optional, data_set);
352  }
353  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_optional",
354  first->uuid, then->uuid,
355  (changed? "changed" : "unchanged"));
356  }
357 
358  if (type & pe_order_asymmetrical) {
359  if (then->rsc) {
360  changed |= then->rsc->cmds->update_actions(first, then, node,
362  data_set);
363  }
364  pe_rsc_trace(then->rsc, "%s then %s: %s after pe_order_asymmetrical",
365  first->uuid, then->uuid,
366  (changed? "changed" : "unchanged"));
367  }
368 
370  && (first_flags & pe_action_optional) == 0) {
371  pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
372  then->uuid, first->uuid);
374  // Don't bother marking 'then' as changed just for this
375  }
376 
378  && !pcmk_is_set(then_flags, pe_action_optional)) {
379 
380  pe_rsc_trace(then->rsc, "%s will be in graph because %s is required",
381  first->uuid, then->uuid);
383  // Don't bother marking 'first' as changed just for this
384  }
385 
388  || type & pe_order_restart)
389  && first->rsc
390  && pcmk__str_eq(first->task, RSC_STOP, pcmk__str_casei)
391  && !pcmk_is_set(first->rsc->flags, pe_rsc_managed)
392  && pcmk_is_set(first->rsc->flags, pe_rsc_block)
393  && !pcmk_is_set(first->flags, pe_action_runnable)) {
394 
395  if (pcmk_is_set(then->flags, pe_action_runnable)) {
398  }
399  pe_rsc_trace(then->rsc, "%s then %s: %s after checking whether first "
400  "is blocked, unmanaged, unrunnable stop",
401  first->uuid, then->uuid,
402  (changed? "changed" : "unchanged"));
403  }
404 
405  return changed;
406 }
407 
408 // Convenience macros for logging action properties
409 
410 #define action_type_str(flags) \
411  (pcmk_is_set((flags), pe_action_pseudo)? "pseudo-action" : "action")
412 
413 #define action_optional_str(flags) \
414  (pcmk_is_set((flags), pe_action_optional)? "optional" : "required")
415 
416 #define action_runnable_str(flags) \
417  (pcmk_is_set((flags), pe_action_runnable)? "runnable" : "unrunnable")
418 
419 #define action_node_str(a) \
420  (((a)->node == NULL)? "no node" : (a)->node->details->uname)
421 
422 gboolean
424 {
425  GList *lpc = NULL;
426  enum pe_graph_flags changed = pe_graph_none;
427  int last_flags = then->flags;
428 
429  pe_rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
430  action_type_str(then->flags), then->uuid,
431  action_optional_str(then->flags),
433 
435  /* initialize current known runnable before actions to 0
436  * from here as graph_update_action is called for each of
437  * then's before actions, this number will increment as
438  * runnable 'first' actions are encountered */
439  then->runnable_before = 0;
440 
441  /* for backwards compatibility with previous options that use
442  * the 'requires_any' flag, initialize required to 1 if it is
443  * not set. */
444  if (then->required_runnable_before == 0) {
445  then->required_runnable_before = 1;
446  }
448  /* We are relying on the pe_order_one_or_more clause of
449  * graph_update_action(), called as part of the:
450  *
451  * 'if (first == other->action)'
452  *
453  * block below, to set this back if appropriate
454  */
455  }
456 
457  for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
458  pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
459  pe_action_t *first = other->action;
460 
461  pe_node_t *then_node = then->node;
462  pe_node_t *first_node = first->node;
463 
464  enum pe_action_flags then_flags = 0;
465  enum pe_action_flags first_flags = 0;
466 
467  if (first->rsc && first->rsc->variant == pe_group && pcmk__str_eq(first->task, RSC_START, pcmk__str_casei)) {
468  first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
469  if (first_node) {
470  pe_rsc_trace(first->rsc, "Found node %s for 'first' %s",
471  first_node->details->uname, first->uuid);
472  }
473  }
474 
475  if (then->rsc && then->rsc->variant == pe_group && pcmk__str_eq(then->task, RSC_START, pcmk__str_casei)) {
476  then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
477  if (then_node) {
478  pe_rsc_trace(then->rsc, "Found node %s for 'then' %s",
479  then_node->details->uname, then->uuid);
480  }
481  }
482  /* Disable constraint if it only applies when on same node, but isn't */
483  if (pcmk_is_set(other->type, pe_order_same_node)
484  && (first_node != NULL) && (then_node != NULL)
485  && (first_node->details != then_node->details)) {
486 
487  pe_rsc_trace(then->rsc,
488  "Disabled ordering %s on %s then %s on %s: not same node",
489  other->action->uuid, first_node->details->uname,
490  then->uuid, then_node->details->uname);
491  other->type = pe_order_none;
492  continue;
493  }
494 
496 
497  if (first->rsc && pcmk_is_set(other->type, pe_order_then_cancels_first)
498  && !pcmk_is_set(then->flags, pe_action_optional)) {
499 
500  /* 'then' is required, so we must abandon 'first'
501  * (e.g. a required stop cancels any agent reload).
502  */
504  if (!strcmp(first->task, CRMD_ACTION_RELOAD_AGENT)) {
506  }
507  }
508 
509  if (first->rsc && then->rsc && (first->rsc != then->rsc)
510  && (is_parent(then->rsc, first->rsc) == FALSE)) {
511  first = rsc_expand_action(first);
512  }
513  if (first != other->action) {
514  pe_rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
515  then->uuid, first->uuid, other->action->uuid);
516  }
517 
518  first_flags = get_action_flags(first, then_node);
519  then_flags = get_action_flags(then, first_node);
520 
521  pe_rsc_trace(then->rsc,
522  "%s then %s: type=0x%.6x filter=0x%.6x "
523  "(%s %s %s on %s 0x%.6x then 0x%.6x)",
524  first->uuid, then->uuid, other->type, first_flags,
525  action_optional_str(first_flags),
526  action_runnable_str(first_flags),
527  action_type_str(first_flags), action_node_str(first),
528  first->flags, then->flags);
529 
530  if (first == other->action) {
531  /*
532  * 'first' was not expanded (e.g. from 'start' to 'running'), which could mean it:
533  * - has no associated resource,
534  * - was a primitive,
535  * - was pre-expanded (e.g. 'running' instead of 'start')
536  *
537  * The third argument here to graph_update_action() is a node which is used under two conditions:
538  * - Interleaving, in which case first->node and
539  * then->node are equal (and NULL)
540  * - If 'then' is a clone, to limit the scope of the
541  * constraint to instances on the supplied node
542  *
543  */
544  pe_node_t *node = then->node;
545  changed |= graph_update_action(first, then, node, first_flags,
546  then_flags, other, data_set);
547 
548  /* 'first' was for a complex resource (clone, group, etc),
549  * create a new dependency if necessary
550  */
551  } else if (order_actions(first, then, other->type)) {
552  /* This was the first time 'first' and 'then' were associated,
553  * start again to get the new actions_before list
554  */
555  pe__set_graph_flags(changed, then,
557  }
558 
559  if (changed & pe_graph_disable) {
560  pe_rsc_trace(then->rsc,
561  "Disabled ordering %s then %s in favor of %s then %s",
562  other->action->uuid, then->uuid, first->uuid,
563  then->uuid);
564  pe__clear_graph_flags(changed, then, pe_graph_disable);
565  other->type = pe_order_none;
566  }
567 
568  if (changed & pe_graph_updated_first) {
569  GList *lpc2 = NULL;
570 
571  crm_trace("Re-processing %s and its 'after' actions since it changed",
572  first->uuid);
573  for (lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) {
574  pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc2->data;
575 
576  update_action(other->action, data_set);
577  }
578  update_action(first, data_set);
579  }
580  }
581 
583  if (last_flags != then->flags) {
585  } else {
587  }
588  }
589 
590  if (changed & pe_graph_updated_then) {
591  crm_trace("Re-processing %s and its 'after' actions since it changed",
592  then->uuid);
593  if (pcmk_is_set(last_flags, pe_action_runnable)
594  && !pcmk_is_set(then->flags, pe_action_runnable)) {
595  pcmk__block_colocated_starts(then, data_set);
596  }
597  update_action(then, data_set);
598  for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
599  pe_action_wrapper_t *other = (pe_action_wrapper_t *) lpc->data;
600 
601  update_action(other->action, data_set);
602  }
603  }
604 
605  return FALSE;
606 }
607 
615 static xmlNode*
616 add_node_to_xml_by_id(const char *id, xmlNode *xml)
617 {
618  xmlNode *node_xml;
619 
620  node_xml = create_xml_node(xml, XML_CIB_TAG_NODE);
621  crm_xml_add(node_xml, XML_ATTR_UUID, id);
622 
623  return node_xml;
624 }
625 
633 static void
634 add_node_to_xml(const pe_node_t *node, void *xml)
635 {
636  add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
637 }
638 
646 static int
647 add_maintenance_nodes(xmlNode *xml, const pe_working_set_t *data_set)
648 {
649  GList *gIter = NULL;
650  xmlNode *maintenance =
652  int count = 0;
653 
654  for (gIter = data_set->nodes; gIter != NULL;
655  gIter = gIter->next) {
656  pe_node_t *node = (pe_node_t *) gIter->data;
657  struct pe_node_shared_s *details = node->details;
658 
659  if (!pe__is_guest_or_remote_node(node)) {
660  continue; /* just remote nodes need to know atm */
661  }
662 
663  if (details->maintenance != details->remote_maintenance) {
664  if (maintenance) {
665  crm_xml_add(
666  add_node_to_xml_by_id(node->details->id, maintenance),
667  XML_NODE_IS_MAINTENANCE, details->maintenance?"1":"0");
668  }
669  count++;
670  }
671  }
672  crm_trace("%s %d nodes to adjust maintenance-mode "
673  "to transition", maintenance?"Added":"Counted", count);
674  return count;
675 }
676 
683 void
685 {
686  pe_action_t *action = NULL;
687 
688  if (add_maintenance_nodes(NULL, data_set)) {
689  crm_trace("adding maintenance state update pseudo action");
692  }
693 }
694 
707 static void
708 add_downed_nodes(xmlNode *xml, const pe_action_t *action,
709  const pe_working_set_t *data_set)
710 {
711  CRM_CHECK(xml && action && action->node && data_set, return);
712 
713  if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
714 
715  /* Shutdown makes the action's node down */
716  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
717  add_node_to_xml_by_id(action->node->details->id, downed);
718 
719  } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
720 
721  /* Fencing makes the action's node and any hosted guest nodes down */
722  const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
723 
724  if (pcmk__strcase_any_of(fence, "off", "reboot", NULL)) {
725  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
726  add_node_to_xml_by_id(action->node->details->id, downed);
727  pe_foreach_guest_node(data_set, action->node, add_node_to_xml, downed);
728  }
729 
730  } else if (action->rsc && action->rsc->is_remote_node
731  && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
732 
733  /* Stopping a remote connection resource makes connected node down,
734  * unless it's part of a migration
735  */
736  GList *iter;
737  pe_action_t *input;
738  gboolean migrating = FALSE;
739 
740  for (iter = action->actions_before; iter != NULL; iter = iter->next) {
741  input = ((pe_action_wrapper_t *) iter->data)->action;
742  if (input->rsc && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_casei)
743  && pcmk__str_eq(input->task, CRMD_ACTION_MIGRATED, pcmk__str_casei)) {
744  migrating = TRUE;
745  break;
746  }
747  }
748  if (!migrating) {
749  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
750  add_node_to_xml_by_id(action->rsc->id, downed);
751  }
752  }
753 }
754 
755 static bool
756 should_lock_action(pe_action_t *action)
757 {
758  // Only actions taking place on resource's lock node are locked
759  if ((action->rsc->lock_node == NULL) || (action->node == NULL)
760  || (action->node->details != action->rsc->lock_node->details)) {
761  return false;
762  }
763 
764  /* During shutdown, only stops are locked (otherwise, another action such as
765  * a demote would cause the controller to clear the lock)
766  */
767  if (action->node->details->shutdown && action->task
768  && strcmp(action->task, RSC_STOP)) {
769  return false;
770  }
771 
772  return true;
773 }
774 
775 static xmlNode *
776 action2xml(pe_action_t * action, gboolean as_input, pe_working_set_t *data_set)
777 {
778  gboolean needs_node_info = TRUE;
779  gboolean needs_maintenance_info = FALSE;
780  xmlNode *action_xml = NULL;
781  xmlNode *args_xml = NULL;
782 #if ENABLE_VERSIONED_ATTRS
783  pe_rsc_action_details_t *rsc_details = NULL;
784 #endif
785 
786  if (action == NULL) {
787  return NULL;
788  }
789 
790  if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
791  /* All fences need node info; guest node fences are pseudo-events */
792  action_xml = create_xml_node(NULL,
796 
797  } else if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
798  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
799 
800  } else if (pcmk__str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT, pcmk__str_casei)) {
801  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
802 
803  } else if (pcmk__str_eq(action->task, CRM_OP_LRM_REFRESH, pcmk__str_casei)) {
804  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
805 
806  } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
807  // CIB-only clean-up for shutdown locks
808  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
809  crm_xml_add(action_xml, PCMK__XA_MODE, XML_TAG_CIB);
810 
811 /* } else if(pcmk__str_eq(action->task, RSC_PROBED, pcmk__str_casei)) { */
812 /* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */
813 
814  } else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
815  if (pcmk__str_eq(action->task, CRM_OP_MAINTENANCE_NODES, pcmk__str_casei)) {
816  needs_maintenance_info = TRUE;
817  }
818  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT);
819  needs_node_info = FALSE;
820 
821  } else {
822  action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
823 
824 #if ENABLE_VERSIONED_ATTRS
825  rsc_details = pe_rsc_action_details(action);
826 #endif
827  }
828 
829  crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
830  crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
831  if (action->rsc != NULL && action->rsc->clone_name != NULL) {
832  char *clone_key = NULL;
833  guint interval_ms;
834 
835  if (pcmk__guint_from_hash(action->meta,
837  &interval_ms) != pcmk_rc_ok) {
838  interval_ms = 0;
839  }
840 
841  if (pcmk__str_eq(action->task, RSC_NOTIFY, pcmk__str_casei)) {
842  const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
843  const char *n_task = g_hash_table_lookup(action->meta, "notify_operation");
844 
845  CRM_CHECK(n_type != NULL, crm_err("No notify type value found for %s", action->uuid));
846  CRM_CHECK(n_task != NULL,
847  crm_err("No notify operation value found for %s", action->uuid));
848  clone_key = pcmk__notify_key(action->rsc->clone_name,
849  n_type, n_task);
850 
851  } else if(action->cancel_task) {
852  clone_key = pcmk__op_key(action->rsc->clone_name,
853  action->cancel_task, interval_ms);
854  } else {
855  clone_key = pcmk__op_key(action->rsc->clone_name,
856  action->task, interval_ms);
857  }
858 
859  CRM_CHECK(clone_key != NULL, crm_err("Could not generate a key for %s", action->uuid));
860  crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
861  crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid);
862  free(clone_key);
863 
864  } else {
865  crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
866  }
867 
868  if (needs_node_info && action->node != NULL) {
870 
871  crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
872  crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
873  if (router_node) {
874  crm_xml_add(action_xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname);
875  }
876 
877  g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET), strdup(action->node->details->uname));
878  g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID), strdup(action->node->details->id));
879  }
880 
881  /* No details if this action is only being listed in the inputs section */
882  if (as_input) {
883  return action_xml;
884  }
885 
886  if (action->rsc && !pcmk_is_set(action->flags, pe_action_pseudo)) {
887  int lpc = 0;
888  xmlNode *rsc_xml = NULL;
889  const char *attr_list[] = {
893  };
894 
895  /* If a resource is locked to a node via shutdown-lock, mark its actions
896  * so the controller can preserve the lock when the action completes.
897  */
898  if (should_lock_action(action)) {
900  (long long) action->rsc->lock_time);
901  }
902 
903  // List affected resource
904 
905  rsc_xml = create_xml_node(action_xml,
906  crm_element_name(action->rsc->xml));
907  if (pcmk_is_set(action->rsc->flags, pe_rsc_orphan)
908  && action->rsc->clone_name) {
909  /* Do not use the 'instance free' name here as that
910  * might interfere with the instance we plan to keep.
911  * Ie. if there are more than two named /anonymous/
912  * instances on a given node, we need to make sure the
913  * command goes to the right one.
914  *
915  * Keep this block, even when everyone is using
916  * 'instance free' anonymous clone names - it means
917  * we'll do the right thing if anyone toggles the
918  * unique flag to 'off'
919  */
920  crm_debug("Using orphan clone name %s instead of %s", action->rsc->id,
921  action->rsc->clone_name);
922  crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
923  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
924 
925  } else if (!pcmk_is_set(action->rsc->flags, pe_rsc_unique)) {
926  const char *xml_id = ID(action->rsc->xml);
927 
928  crm_debug("Using anonymous clone name %s for %s (aka. %s)", xml_id, action->rsc->id,
929  action->rsc->clone_name);
930 
931  /* ID is what we'd like client to use
932  * ID_LONG is what they might know it as instead
933  *
934  * ID_LONG is only strictly needed /here/ during the
935  * transition period until all nodes in the cluster
936  * are running the new software /and/ have rebooted
937  * once (meaning that they've only ever spoken to a DC
938  * supporting this feature).
939  *
940  * If anyone toggles the unique flag to 'on', the
941  * 'instance free' name will correspond to an orphan
942  * and fall into the clause above instead
943  */
944  crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
945  if (action->rsc->clone_name && !pcmk__str_eq(xml_id, action->rsc->clone_name, pcmk__str_casei)) {
946  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
947  } else {
948  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
949  }
950 
951  } else {
952  CRM_ASSERT(action->rsc->clone_name == NULL);
953  crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
954  }
955 
956  for (lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
957  crm_xml_add(rsc_xml, attr_list[lpc],
958  g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
959  }
960  }
961 
962  /* List any attributes in effect */
963  args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
965 
966  g_hash_table_foreach(action->extra, hash2field, args_xml);
967  if (action->rsc != NULL && action->node) {
968  // Get the resource instance attributes, evaluated properly for node
969  GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
970 
971  pcmk__substitute_remote_addr(action->rsc, params, data_set);
972 
973  g_hash_table_foreach(params, hash2smartfield, args_xml);
974 
975 #if ENABLE_VERSIONED_ATTRS
976  {
977  xmlNode *versioned_parameters = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
978 
979  pe_get_versioned_attributes(versioned_parameters, action->rsc,
980  action->node, data_set);
981  if (xml_has_children(versioned_parameters)) {
982  add_node_copy(action_xml, versioned_parameters);
983  }
984  free_xml(versioned_parameters);
985  }
986 #endif
987 
988  } else if(action->rsc && action->rsc->variant <= pe_native) {
989  GHashTable *params = pe_rsc_params(action->rsc, NULL, data_set);
990 
991  g_hash_table_foreach(params, hash2smartfield, args_xml);
992 
993 #if ENABLE_VERSIONED_ATTRS
994  if (xml_has_children(action->rsc->versioned_parameters)) {
995  add_node_copy(action_xml, action->rsc->versioned_parameters);
996  }
997 #endif
998  }
999 
1000 #if ENABLE_VERSIONED_ATTRS
1001  if (rsc_details) {
1002  if (xml_has_children(rsc_details->versioned_parameters)) {
1003  add_node_copy(action_xml, rsc_details->versioned_parameters);
1004  }
1005 
1006  if (xml_has_children(rsc_details->versioned_meta)) {
1007  add_node_copy(action_xml, rsc_details->versioned_meta);
1008  }
1009  }
1010 #endif
1011 
1012  g_hash_table_foreach(action->meta, hash2metafield, args_xml);
1013  if (action->rsc != NULL) {
1014  const char *value = g_hash_table_lookup(action->rsc->meta, "external-ip");
1015  pe_resource_t *parent = action->rsc;
1016 
1017  while (parent != NULL) {
1018  parent->cmds->append_meta(parent, args_xml);
1019  parent = parent->parent;
1020  }
1021 
1022  if(value) {
1023  hash2smartfield((gpointer)"pcmk_external_ip", (gpointer)value, (gpointer)args_xml);
1024  }
1025 
1027 
1028  } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei) && action->node) {
1029  /* Pass the node's attributes as meta-attributes.
1030  *
1031  * @TODO: Determine whether it is still necessary to do this. It was
1032  * added in 33d99707, probably for the libfence-based implementation in
1033  * c9a90bd, which is no longer used.
1034  */
1035  g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
1036  }
1037 
1038  sorted_xml(args_xml, action_xml, FALSE);
1039  free_xml(args_xml);
1040 
1041  /* List any nodes this action is expected to make down */
1042  if (needs_node_info && (action->node != NULL)) {
1043  add_downed_nodes(action_xml, action, data_set);
1044  }
1045 
1046  if (needs_maintenance_info) {
1047  add_maintenance_nodes(action_xml, data_set);
1048  }
1049 
1050  crm_log_xml_trace(action_xml, "dumped action");
1051  return action_xml;
1052 }
1053 
1054 static bool
1055 should_dump_action(pe_action_t *action)
1056 {
1057  CRM_CHECK(action != NULL, return false);
1058 
1059  if (pcmk_is_set(action->flags, pe_action_dumped)) {
1060  crm_trace("Action %s (%d) already dumped", action->uuid, action->id);
1061  return false;
1062 
1063  } else if (pcmk_is_set(action->flags, pe_action_pseudo)
1064  && pcmk__str_eq(action->task, CRM_OP_PROBED, pcmk__str_casei)) {
1065  GList *lpc = NULL;
1066 
1067  /* This is a horrible but convenient hack
1068  *
1069  * It mimimizes the number of actions with unsatisfied inputs
1070  * (i.e. not included in the graph)
1071  *
1072  * This in turn, means we can be more concise when printing
1073  * aborted/incomplete graphs.
1074  *
1075  * It also makes it obvious which node is preventing
1076  * probe_complete from running (presumably because it is only
1077  * partially up)
1078  *
1079  * For these reasons we tolerate such perversions
1080  */
1081 
1082  for (lpc = action->actions_after; lpc != NULL; lpc = lpc->next) {
1083  pe_action_wrapper_t *wrapper = (pe_action_wrapper_t *) lpc->data;
1084 
1085  if (!pcmk_is_set(wrapper->action->flags, pe_action_runnable)) {
1086  /* Only interested in runnable operations */
1087  } else if (!pcmk__str_eq(wrapper->action->task, RSC_START, pcmk__str_casei)) {
1088  /* Only interested in start operations */
1089  } else if (pcmk_is_set(wrapper->action->flags, pe_action_dumped)
1090  || should_dump_action(wrapper->action)) {
1091  crm_trace("Action %s (%d) should be dumped: "
1092  "dependency of %s (%d)",
1093  action->uuid, action->id,
1094  wrapper->action->uuid, wrapper->action->id);
1095  return true;
1096  }
1097  }
1098  }
1099 
1100  if (!pcmk_is_set(action->flags, pe_action_runnable)) {
1101  crm_trace("Ignoring action %s (%d): unrunnable",
1102  action->uuid, action->id);
1103  return false;
1104 
1105  } else if (pcmk_is_set(action->flags, pe_action_optional)
1106  && !pcmk_is_set(action->flags, pe_action_print_always)) {
1107  crm_trace("Ignoring action %s (%d): optional",
1108  action->uuid, action->id);
1109  return false;
1110 
1111  // Monitors should be dumped even for unmanaged resources
1112  } else if (action->rsc && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
1113  && !pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_casei)) {
1114 
1115  const char *interval_ms_s = g_hash_table_lookup(action->meta,
1117 
1118  // Cancellation of recurring monitors should still be dumped
1119  if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
1120  crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
1121  action->uuid, action->id, action->rsc->id);
1122  return false;
1123  }
1124  }
1125 
1126  if (pcmk_is_set(action->flags, pe_action_pseudo) ||
1128  /* skip the next checks */
1129  return true;
1130  }
1131 
1132  if (action->node == NULL) {
1133  pe_err("Skipping action %s (%d) "
1134  "because it was not allocated to a node (bug?)",
1135  action->uuid, action->id);
1136  log_action(LOG_DEBUG, "Unallocated action", action, false);
1137  return false;
1138 
1139  } else if (pcmk_is_set(action->flags, pe_action_dc)) {
1140  crm_trace("Action %s (%d) should be dumped: "
1141  "can run on DC instead of %s",
1142  action->uuid, action->id, action->node->details->uname);
1143 
1144  } else if (pe__is_guest_node(action->node)
1145  && !action->node->details->remote_requires_reset) {
1146  crm_trace("Action %s (%d) should be dumped: "
1147  "assuming will be runnable on guest node %s",
1148  action->uuid, action->id, action->node->details->uname);
1149 
1150  } else if (action->node->details->online == false) {
1151  pe_err("Skipping action %s (%d) "
1152  "because it was scheduled for offline node (bug?)",
1153  action->uuid, action->id);
1154  log_action(LOG_DEBUG, "Action for offline node", action, FALSE);
1155  return false;
1156 #if 0
1157  /* but this would also affect resources that can be safely
1158  * migrated before a fencing op
1159  */
1160  } else if (action->node->details->unclean == false) {
1161  pe_err("Skipping action %s (%d) "
1162  "because it was scheduled for unclean node (bug?)",
1163  action->uuid, action->id);
1164  log_action(LOG_DEBUG, "Action for unclean node", action, false);
1165  return false;
1166 #endif
1167  }
1168  return true;
1169 }
1170 
1171 /* lowest to highest */
1172 static gint
1173 sort_action_id(gconstpointer a, gconstpointer b)
1174 {
1175  const pe_action_wrapper_t *action_wrapper2 = (const pe_action_wrapper_t *)a;
1176  const pe_action_wrapper_t *action_wrapper1 = (const pe_action_wrapper_t *)b;
1177 
1178  if (a == NULL) {
1179  return 1;
1180  }
1181  if (b == NULL) {
1182  return -1;
1183  }
1184 
1185  if (action_wrapper1->action->id > action_wrapper2->action->id) {
1186  return -1;
1187  }
1188 
1189  if (action_wrapper1->action->id < action_wrapper2->action->id) {
1190  return 1;
1191  }
1192  return 0;
1193 }
1194 
1203 static bool
1204 ordering_can_change_actions(pe_action_wrapper_t *ordering)
1205 {
1206  return pcmk_any_flags_set(ordering->type, ~(pe_order_implies_first_printed
1208  |pe_order_optional));
1209 }
1210 
1222 static bool
1223 check_dump_input(pe_action_t *action, pe_action_wrapper_t *input)
1224 {
1225  if (input->state == pe_link_dumped) {
1226  return true;
1227  }
1228 
1229  if (input->type == pe_order_none) {
1230  crm_trace("Ignoring %s (%d) input %s (%d): "
1231  "ordering disabled",
1232  action->uuid, action->id,
1233  input->action->uuid, input->action->id);
1234  return false;
1235 
1236  } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
1237  && !ordering_can_change_actions(input)
1238  && !pcmk__str_eq(input->action->uuid, CRM_OP_PROBED, pcmk__str_casei)) {
1239  crm_trace("Ignoring %s (%d) input %s (%d): "
1240  "optional and input unrunnable",
1241  action->uuid, action->id,
1242  input->action->uuid, input->action->id);
1243  return false;
1244 
1245  } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
1246  && pcmk_is_set(input->type, pe_order_one_or_more)) {
1247  crm_trace("Ignoring %s (%d) input %s (%d): "
1248  "one-or-more and input unrunnable",
1249  action->uuid, action->id,
1250  input->action->uuid, input->action->id);
1251  return false;
1252 
1253  } else if (pcmk_is_set(action->flags, pe_action_pseudo)
1254  && pcmk_is_set(input->type, pe_order_stonith_stop)) {
1255  crm_trace("Ignoring %s (%d) input %s (%d): "
1256  "stonith stop but action is pseudo",
1257  action->uuid, action->id,
1258  input->action->uuid, input->action->id);
1259  return false;
1260 
1262  && !pcmk_is_set(input->action->flags, pe_action_runnable)) {
1263  crm_trace("Ignoring %s (%d) input %s (%d): "
1264  "implies input migratable but input unrunnable",
1265  action->uuid, action->id,
1266  input->action->uuid, input->action->id);
1267  return false;
1268 
1271  crm_trace("Ignoring %s (%d) input %s (%d): "
1272  "only if input unmigratable but input unrunnable",
1273  action->uuid, action->id,
1274  input->action->uuid, input->action->id);
1275  return false;
1276 
1277  } else if ((input->type == pe_order_optional)
1279  && pcmk__ends_with(input->action->uuid, "_stop_0")) {
1280  crm_trace("Ignoring %s (%d) input %s (%d): "
1281  "optional but stop in migration",
1282  action->uuid, action->id,
1283  input->action->uuid, input->action->id);
1284  return false;
1285 
1286  } else if (input->type == pe_order_load) {
1287  pe_node_t *input_node = input->action->node;
1288 
1289  // load orderings are relevant only if actions are for same node
1290 
1291  if (action->rsc && pcmk__str_eq(action->task, RSC_MIGRATE, pcmk__str_casei)) {
1292  pe_node_t *allocated = action->rsc->allocated_to;
1293 
1294  /* For load_stopped -> migrate_to orderings, we care about where it
1295  * has been allocated to, not where it will be executed.
1296  */
1297  if ((input_node == NULL) || (allocated == NULL)
1298  || (input_node->details != allocated->details)) {
1299  crm_trace("Ignoring %s (%d) input %s (%d): "
1300  "load ordering node mismatch %s vs %s",
1301  action->uuid, action->id,
1302  input->action->uuid, input->action->id,
1303  (allocated? allocated->details->uname : "<none>"),
1304  (input_node? input_node->details->uname : "<none>"));
1305  input->type = pe_order_none;
1306  return false;
1307  }
1308 
1309  } else if ((input_node == NULL) || (action->node == NULL)
1310  || (input_node->details != action->node->details)) {
1311  crm_trace("Ignoring %s (%d) input %s (%d): "
1312  "load ordering node mismatch %s vs %s",
1313  action->uuid, action->id,
1314  input->action->uuid, input->action->id,
1315  (action->node? action->node->details->uname : "<none>"),
1316  (input_node? input_node->details->uname : "<none>"));
1317  input->type = pe_order_none;
1318  return false;
1319 
1320  } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
1321  crm_trace("Ignoring %s (%d) input %s (%d): "
1322  "load ordering input optional",
1323  action->uuid, action->id,
1324  input->action->uuid, input->action->id);
1325  input->type = pe_order_none;
1326  return false;
1327  }
1328 
1329  } else if (input->type == pe_order_anti_colocation) {
1330  if (input->action->node && action->node
1331  && (input->action->node->details != action->node->details)) {
1332  crm_trace("Ignoring %s (%d) input %s (%d): "
1333  "anti-colocation node mismatch %s vs %s",
1334  action->uuid, action->id,
1335  input->action->uuid, input->action->id,
1336  action->node->details->uname,
1337  input->action->node->details->uname);
1338  input->type = pe_order_none;
1339  return false;
1340 
1341  } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
1342  crm_trace("Ignoring %s (%d) input %s (%d): "
1343  "anti-colocation input optional",
1344  action->uuid, action->id,
1345  input->action->uuid, input->action->id);
1346  input->type = pe_order_none;
1347  return false;
1348  }
1349 
1350  } else if (input->action->rsc
1351  && input->action->rsc != action->rsc
1352  && pcmk_is_set(input->action->rsc->flags, pe_rsc_failed)
1353  && !pcmk_is_set(input->action->rsc->flags, pe_rsc_managed)
1354  && pcmk__ends_with(input->action->uuid, "_stop_0")
1355  && action->rsc && pe_rsc_is_clone(action->rsc)) {
1356  crm_warn("Ignoring requirement that %s complete before %s:"
1357  " unmanaged failed resources cannot prevent clone shutdown",
1358  input->action->uuid, action->uuid);
1359  return false;
1360 
1361  } else if (pcmk_is_set(input->action->flags, pe_action_optional)
1362  && !pcmk_any_flags_set(input->action->flags,
1364  && !should_dump_action(input->action)) {
1365  crm_trace("Ignoring %s (%d) input %s (%d): "
1366  "input optional",
1367  action->uuid, action->id,
1368  input->action->uuid, input->action->id);
1369  return false;
1370  }
1371 
1372  crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s 0x%.6x",
1373  action->uuid, action->id, action_type_str(input->action->flags),
1374  input->action->uuid, input->action->id,
1375  action_node_str(input->action),
1376  action_runnable_str(input->action->flags),
1377  action_optional_str(input->action->flags), input->type);
1378  return true;
1379 }
1380 
1381 bool
1383  pe_action_wrapper_t *input)
1384 {
1385  bool has_loop = false;
1386 
1387  if (pcmk_is_set(input->action->flags, pe_action_tracking)) {
1388  crm_trace("Breaking tracking loop: %s@%s -> %s@%s (0x%.6x)",
1389  input->action->uuid,
1390  input->action->node? input->action->node->details->uname : "",
1391  action->uuid,
1392  action->node? action->node->details->uname : "",
1393  input->type);
1394  return false;
1395  }
1396 
1397  // Don't need to check inputs that won't be used
1398  if (!check_dump_input(action, input)) {
1399  return false;
1400  }
1401 
1402  if (input->action == init_action) {
1403  crm_debug("Input loop found in %s@%s ->...-> %s@%s",
1404  action->uuid,
1405  action->node? action->node->details->uname : "",
1406  init_action->uuid,
1407  init_action->node? init_action->node->details->uname : "");
1408  return true;
1409  }
1410 
1412 
1413  crm_trace("Checking inputs of action %s@%s input %s@%s (0x%.6x)"
1414  "for graph loop with %s@%s ",
1415  action->uuid,
1416  action->node? action->node->details->uname : "",
1417  input->action->uuid,
1418  input->action->node? input->action->node->details->uname : "",
1419  input->type,
1420  init_action->uuid,
1421  init_action->node? init_action->node->details->uname : "");
1422 
1423  // Recursively check input itself for loops
1424  for (GList *iter = input->action->actions_before;
1425  iter != NULL; iter = iter->next) {
1426 
1427  if (pcmk__graph_has_loop(init_action, input->action,
1428  (pe_action_wrapper_t *) iter->data)) {
1429  // Recursive call already logged a debug message
1430  has_loop = true;
1431  goto done;
1432  }
1433  }
1434 
1435 done:
1437 
1438  if (!has_loop) {
1439  crm_trace("No input loop found in %s@%s -> %s@%s (0x%.6x)",
1440  input->action->uuid,
1441  input->action->node? input->action->node->details->uname : "",
1442  action->uuid,
1443  action->node? action->node->details->uname : "",
1444  input->type);
1445  }
1446  return has_loop;
1447 }
1448 
1449 // Remove duplicate inputs (regardless of flags)
1450 static void
1451 deduplicate_inputs(pe_action_t *action)
1452 {
1453  GList *item = NULL;
1454  GList *next = NULL;
1455  pe_action_wrapper_t *last_input = NULL;
1456 
1457  action->actions_before = g_list_sort(action->actions_before,
1458  sort_action_id);
1459  for (item = action->actions_before; item != NULL; item = next) {
1460  pe_action_wrapper_t *input = (pe_action_wrapper_t *) item->data;
1461 
1462  next = item->next;
1463  if (last_input && (input->action->id == last_input->action->id)) {
1464  crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1465  input->action->uuid, input->action->id,
1466  action->uuid, action->id);
1467 
1468  /* For the purposes of scheduling, the ordering flags no longer
1469  * matter, but crm_simulate looks at certain ones when creating a
1470  * dot graph. Combining the flags is sufficient for that purpose.
1471  */
1472  last_input->type |= input->type;
1473  if (input->state == pe_link_dumped) {
1474  last_input->state = pe_link_dumped;
1475  }
1476 
1477  free(item->data);
1478  action->actions_before = g_list_delete_link(action->actions_before,
1479  item);
1480  } else {
1481  last_input = input;
1482  input->state = pe_link_not_dumped;
1483  }
1484  }
1485 }
1486 
1503 void
1505 {
1506  GList *lpc = NULL;
1507  int synapse_priority = 0;
1508  xmlNode *syn = NULL;
1509  xmlNode *set = NULL;
1510  xmlNode *in = NULL;
1511  xmlNode *xml_action = NULL;
1512  pe_action_wrapper_t *input = NULL;
1513 
1514  /* If we haven't already, de-duplicate inputs -- even if we won't be dumping
1515  * the action, so that crm_simulate dot graphs don't have duplicates.
1516  */
1517  if (!pcmk_is_set(action->flags, pe_action_dedup)) {
1518  deduplicate_inputs(action);
1520  }
1521 
1522  if (should_dump_action(action) == FALSE) {
1523  return;
1524  }
1525 
1527 
1528  syn = create_xml_node(data_set->graph, "synapse");
1529  set = create_xml_node(syn, "action_set");
1530  in = create_xml_node(syn, "inputs");
1531 
1532  crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse);
1533  data_set->num_synapse++;
1534 
1535  if (action->rsc != NULL) {
1536  synapse_priority = action->rsc->priority;
1537  }
1538  if (action->priority > synapse_priority) {
1539  synapse_priority = action->priority;
1540  }
1541  if (synapse_priority > 0) {
1542  crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
1543  }
1544 
1545  xml_action = action2xml(action, FALSE, data_set);
1546  add_node_nocopy(set, crm_element_name(xml_action), xml_action);
1547 
1548  for (lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
1549  input = (pe_action_wrapper_t *) lpc->data;
1550  if (check_dump_input(action, input)) {
1551  xmlNode *input_xml = create_xml_node(in, "trigger");
1552 
1553  input->state = pe_link_dumped;
1554  xml_action = action2xml(input->action, TRUE, data_set);
1555  add_node_nocopy(input_xml, crm_element_name(xml_action), xml_action);
1556  }
1557  }
1558 }
#define XML_ATTR_ID_LONG
Definition: msg_xml.h:131
enum pe_link_state state
Definition: pe_types.h:533
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pe_resource_t *rsc, GHashTable *params, pe_working_set_t *data_set)
const char * task2text(enum action_tasks task)
Definition: common.c:406
#define RSC_STOP
Definition: crm.h:204
A dumping ground.
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:185
#define CRMD_ACTION_MIGRATED
Definition: crm.h:174
bool pe__is_guest_or_remote_node(const pe_node_t *node)
Definition: remote.c:41
#define XML_CONFIG_ATTR_SHUTDOWN_LOCK
Definition: msg_xml.h:387
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:59
gboolean is_parent(pe_resource_t *child, pe_resource_t *rsc)
Definition: complex.c:886
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:785
int runnable_before
Definition: pe_types.h:442
#define CRM_OP_FENCE
Definition: crm.h:145
#define XML_GRAPH_TAG_MAINTENANCE
Definition: msg_xml.h:330
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:931
#define action_type_str(flags)
#define XML_ATTR_TYPE
Definition: msg_xml.h:132
resource_alloc_functions_t * cmds
Definition: pe_types.h:334
Internal tracking for transition graph creation.
Definition: pe_types.h:472
bool pcmk__graph_has_loop(pe_action_t *init_action, pe_action_t *action, pe_action_wrapper_t *input)
enum pe_graph_flags(* update_actions)(pe_action_t *, pe_action_t *, pe_node_t *, enum pe_action_flags, enum pe_action_flags, enum pe_ordering, pe_working_set_t *data_set)
pe_resource_t * rsc
Definition: pe_types.h:410
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:431
void pe_foreach_guest_node(const pe_working_set_t *data_set, const pe_node_t *host, void(*helper)(const pe_node_t *, void *), void *user_data)
Definition: remote.c:120
#define CRM_FEATURE_SET
Definition: crm.h:69
#define pe_rsc_unique
Definition: pe_types.h:254
#define pe_rsc_notify
Definition: pe_types.h:253
#define XML_TAG_ATTRS
Definition: msg_xml.h:205
resource_object_functions_t * fns
Definition: pe_types.h:333
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
#define pe__clear_graph_flags(graph_flags, gr_action, flags_to_clear)
Definition: internal.h:132
#define XML_GRAPH_TAG_RSC_OP
Definition: msg_xml.h:326
#define XML_NODE_IS_MAINTENANCE
Definition: msg_xml.h:281
enum action_tasks text2task(const char *task)
Definition: common.c:354
#define XML_GRAPH_TAG_CRM_EVENT
Definition: msg_xml.h:328
enum crm_ais_msg_types type
Definition: cpg.c:48
#define RSC_START
Definition: crm.h:201
void add_maintenance_update(pe_working_set_t *data_set)
pe_action_t * action
Definition: pe_types.h:534
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:536
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:751
#define XML_GRAPH_TAG_DOWNED
Definition: msg_xml.h:329
#define pe__set_raw_action_flags(action_flags, action_name, flags_to_set)
Definition: internal.h:77
#define RSC_NOTIFY
Definition: crm.h:212
#define RSC_MIGRATE
Definition: crm.h:198
const char * action
Definition: pcmk_fence.c:30
#define XML_GRAPH_TAG_PSEUDO_EVENT
Definition: msg_xml.h:327
GList * nodes
Definition: pe_types.h:157
#define XML_CIB_ATTR_PRIORITY
Definition: msg_xml.h:272
G_GNUC_INTERNAL pe_node_t * pcmk__connection_host_for_action(pe_action_t *action)
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:298
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:297
#define CRM_OP_LRM_REFRESH
Definition: crm.h:149
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:54
#define CRMD_ACTION_STOP
Definition: crm.h:179
#define CRM_OP_CLEAR_FAILCOUNT
Definition: crm.h:155
gboolean update_action(pe_action_t *then, pe_working_set_t *data_set)
#define crm_warn(fmt, args...)
Definition: logging.h:358
#define CRMD_ACTION_RELOAD_AGENT
Definition: crm.h:172
pe_action_flags
Definition: pe_types.h:291
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:229
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:311
#define pe_rsc_failed
Definition: pe_types.h:267
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition: utils.c:1952
#define crm_debug(fmt, args...)
Definition: logging.h:362
gboolean rsc_update_action(pe_action_t *first, pe_action_t *then, enum pe_ordering type)
#define pe__clear_order_flags(order_flags, flags_to_clear)
Definition: internal.h:118
G_GNUC_INTERNAL void pcmk__block_colocated_starts(pe_action_t *action, pe_working_set_t *data_set)
#define XML_ATTR_ID
Definition: msg_xml.h:129
bool pe__is_guest_node(const pe_node_t *node)
Definition: remote.c:33
char * task
Definition: pe_types.h:414
GList * actions_after
Definition: pe_types.h:448
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:68
#define crm_trace(fmt, args...)
Definition: logging.h:363
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:674
struct pe_node_shared_s * details
Definition: pe_types.h:244
pe_node_t * node
Definition: pe_types.h:411
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:267
gboolean order_actions(pe_action_t *lh_action, pe_action_t *rh_action, enum pe_ordering order)
Definition: utils.c:1905
unsigned long long flags
Definition: pe_types.h:348
const char * uname
Definition: pe_types.h:209
GHashTable * pe_rsc_params(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition: complex.c:457
Wrappers for and extensions to libxml2.
Internal state tracking when creating graph.
Definition: pe_types.h:317
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:696
#define PCMK__NELEM(a)
Definition: internal.h:40
#define pe__set_graph_flags(graph_flags, gr_action, flags_to_set)
Definition: internal.h:125
GList * actions
Definition: pe_types.h:359
pe_graph_flags
Definition: pe_types.h:283
void(* append_meta)(pe_resource_t *rsc, xmlNode *xml)
char * uuid
Definition: pe_types.h:415
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:305
#define XML_TAG_RSC_VER_ATTRS
Definition: msg_xml.h:206
void free_xml(xmlNode *child)
Definition: xml.c:824
enum pe_obj_types variant
Definition: pe_types.h:331
gboolean xml_has_children(const xmlNode *root)
Definition: xml.c:2023
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:199
#define CRM_OP_SHUTDOWN
Definition: crm.h:144
const char * id
Definition: pe_types.h:208
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
#define CRM_OP_MAINTENANCE_NODES
Definition: crm.h:160
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2757
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:300
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:481
#define XML_ATTR_UUID
Definition: msg_xml.h:152
#define XML_TAG_CIB
Definition: msg_xml.h:109
void log_action(unsigned int log_level, const char *pre_text, pe_action_t *action, gboolean details)
G_GNUC_INTERNAL void pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, pe_action_t *action)
Add special bundle meta-attributes to XML.
#define crm_err(fmt, args...)
Definition: logging.h:357
#define CRM_ASSERT(expr)
Definition: results.h:42
#define RSC_STATUS
Definition: crm.h:215
Cluster Configuration.
#define pe_rsc_reload
Definition: pe_types.h:263
#define PCMK__XA_MODE
Definition: crm_internal.h:69
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:295
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:813
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:53
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:112
GList * running_on
Definition: pe_types.h:366
#define pe_rsc_block
Definition: pe_types.h:250
enum pe_action_flags flags
Definition: pe_types.h:419
#define CRM_OP_PROBED
Definition: crm.h:153
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:688
#define crm_log_xml_trace(xml, text)
Definition: logging.h:371
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:299
#define action_optional_str(flags)
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
#define action_node_str(a)
#define pe__set_order_flags(order_flags, flags_to_set)
Definition: internal.h:111
#define ID(x)
Definition: msg_xml.h:456
#define pe_err(fmt...)
Definition: internal.h:22
void graph_element_from_action(pe_action_t *action, pe_working_set_t *data_set)
#define action_runnable_str(flags)
#define CRM_OP_LRM_DELETE
Definition: crm.h:151
enum pe_ordering type
Definition: pe_types.h:532
#define pe_rsc_managed
Definition: pe_types.h:249
#define pe_rsc_orphan
Definition: pe_types.h:248
pe_action_t * find_first_action(GList *input, const char *uuid, const char *task, pe_node_t *on_node)
Definition: utils.c:1540
pe_ordering
Definition: pe_types.h:483
uint64_t flags
Definition: remote.c:149
GList * actions_before
Definition: pe_types.h:447
int required_runnable_before
Definition: pe_types.h:445
pe_resource_t * parent
Definition: pe_types.h:329
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:266
xmlNode * graph
Definition: pe_types.h:176
char * id
Definition: pe_types.h:322